Слайд 1
Программирование на С++
Тема1.2 Классы и объекты
Слайд 2
#include
#include
using namespace std;
class Distance // Класс английских мер длины
{ private:
int feet; // футы
float inches; // дюймы
public:
Distance():feet(0),inches(0.0){}
Distance(int ft,float in):feet(ft),inches(in){}
void getdist(); void showdist();
//сложение двух длин
void add_dist(Distance d1,Distance d2);
};
Передача объектов в функции
Пример 1. Передача объектов по значению
Слайд 3void Distance::getdist()
{cout > feet;
cout > inches;
}
void Distance::showdist()
{ cout
<< feet << "\'-" << inches << '\"'; }
void Distance::add_dist(Distance d1, Distance d2)
{inches = d1.inches + d2.inches;//сложение дюймов
feet = 0; //с возможным заемом
if (inches >= 12.0)
{ inches -= 12.0; // уменьшаем дюймы на 12
feet++; } // и увеличиваем футы на 1
feet += d1.feet + d2.feet;// сложение футов
}
Слайд 4
int main()
{ Distance dist1, dist3;
dist1.getdist();
Distance dist2(11,6.25);
dist3.add_dist(dist1,
dist2);
cout
<< endl;
cout << "dist2 = ";
dist2.showdist();cout << endl;
cout << "dist3 = ";
dist3.showdist();cout << endl;
getch();
}
Слайд 5Синтаксически объекты передаются в функции так же, как и переменные
других типов. Для этого объявляют параметр функции, который имеет тип
класса. Затем используют объект этого класса в качестве аргумента при вызове функции. По умолчанию происходит передача объекта по значению.
При передаче объекта в функцию по значению создается копия этого объекта. При завершении работы функции созданная копия объекта удаляется. При вызове функции, в момент создания копии объекта, обычный конструктор не вызывается, так как он используется для инициализации элементов объекта, а копия создается для уже существующего (а значит – проинициализированного) объекта. Вызывается конструктор копирования.
Но при завершении работы функции, т. е. при удалении копии объекта, вызывается деструктор, так как иначе могут оказаться невыполненными некоторые необходимые действия, связанные с его удалением, например освобождение памяти.
Здесь возникает проблема освобождения динамической памяти.
Поскольку объект и его копия, если они содержат указатели, обращаются к одной и той же области памяти, то все изменения, производимые с этой памятью копией, будут влиять на объект-оригинал. Кроме того, освобождение памяти деструктором копии приводит к тому, что указатель объекта может стать неопределенным.
Одним из способов обойти эту проблему является передача объектов в функцию по указателю (по ссылке) , другой способ программировать конструктор копирования.
При передаче объекта в функцию по указателю происходит передача его адреса, а не значения. При этом функция может изменять значение объекта, используемого в вызове.
Ссылка является скрытым указателем и всегда работает просто как другое имя переменной. При передаче параметра по ссылке изменения объекта внутри функции влияют на исходный объект и сохраняются при завершении работы функции.
Ссылка не является аналогом указателя, поэтому при передаче объекта по ссылке для доступа к его элементам используется операция «точка » (.), а не «стрелка» (->).
Слайд 6
#include
#include
using namespace std;
class Distance // Класс английских мер длины
{ private:
int feet; // футы
float inches; // дюймы
public:
Distance():feet(0),inches(0.0){}
Distance(int ft,float in):feet(ft),inches(in){}
void getdist(); void showdist();
//сложение двух длин
void add_dist(Distance *d1,Distance *d2);
};
Пример 2. Передача объектов по указателю
Слайд 7void Distance::getdist()
{cout > feet;
cout > inches;
}
void Distance::showdist()
{ cout
<< feet << "\'-" << inches << '\"'; }
void Distance::add_dist(Distance *d1, Distance *d2)
{inches = d1->inches + d2->inches;//сложение дюймов
feet = 0; //с возможным заемом
if (inches >= 12.0)
{ inches -= 12.0; // уменьшаем дюймы на 12
feet++; } // и увеличиваем футы на 1
feet += d1->feet + d2->feet;// сложение футов
}
Слайд 8
int main()
{ Distance dist1, dist3;
dist1.getdist();
Distance dist2(11,6.25);
dist3.add_dist(&dist1,
&dist2);
cout
<< endl;
cout << "dist2 = ";
dist2.showdist();cout << endl;
cout << "dist3 = ";
dist3.showdist();cout << endl;
getch();
}
Слайд 9
#include
#include
using namespace std;
class Distance // Класс английских мер длины
{ private:
int feet; // футы
float inches; // дюймы
public:
Distance():feet(0),inches(0.0){}
Distance(int ft,float in):feet(ft),inches(in){}
void getdist(); void showdist();
//сложение двух длин
void add_dist(Distance &d1,Distance &d2);
};
Пример 3. Передача объектов по ссылке
Слайд 10void Distance::getdist()
{cout > feet;
cout > inches;
}
void Distance::showdist()
{ cout
<< feet << "\'-" << inches << '\"'; }
void Distance::add_dist(Distance &d1, Distance &d2)
{inches = d1.inches + d2.inches;//сложение дюймов
feet = 0; //с возможным заемом
if (inches >= 12.0)
{ inches -= 12.0; // уменьшаем дюймы на 12
feet++; } // и увеличиваем футы на 1
feet += d1.feet + d2.feet;// сложение футов
}
Слайд 11
int main()
{ Distance dist1, dist3;
dist1.getdist();
Distance dist2(11,6.25);
dist3.add_dist(dist1, dist2);
cout
dist1.showdist();cout << endl;
cout << "dist2 = ";
dist2.showdist();cout << endl;
cout << "dist3 = ";
dist3.showdist();cout << endl;
getch();
}
Слайд 12
#include
#include
using namespace std;
class Distance // Класс английских мер длины
{ private:
int feet; // футы
float inches; // дюймы
public:
Distance():feet(0),inches(0.0){}
Distance(int ft,float in):feet(ft),inches(in){}
void getdist(); void showdist();
//сложение двух длин
Distance add_dist(Distance d2);
};
Пример 4. Объект в качестве возвращаемого значения
Слайд 13
void Distance::getdist()
{cout
>> feet;
cout > inches;}
void
Distance::showdist()
{ cout << feet << "\'-" << inches << '\"'; }
Distance Distance::add_dist(Distance d2)
{int f = feet +d2.feet; // складываем футы
float i = inches +d2.inches;//складываем дюймы
if (i >= 12.0)
{ i-=12.0; // уменьшаем дюймы на 12
f++; } // и увеличиваем футы на 1
Distance D(f, i) //создаем и
return D; // возвращаем временный объект
}
Слайд 14
int main()
{ Distance dist1, dist3;
dist1.getdist();
Distance dist2(11,6.25);
dist3 = dist1.add_dist(dist2);
cout
dist1.showdist();cout << endl;
cout << "dist2 = ";
dist2.showdist();cout << endl;
cout << "dist3 = ";
dist3.showdist();cout << endl;
getch();
}
Слайд 15Для того чтобы функция могла возвращать объект, нужно объявить ее
так, чтобы ее возвращаемое значение имело тип класса, а затем
поставить объект этого типа в операторе return.
Когда функция возвращает объект, для его хранения автоматически создается временный объект. После того как значение возвращено, этот временный объект удаляется. Если возвращаемый объект содержит указатели, то удаление его временной копии может привести к неожиданным и печальным последствиям, связанным с особенностями вызова конструктора и деструктора, поэтому необходимо программировать конструктор копирования.
Слайд 16
Дружественные функции
Синтаксис объявления дружественных функций:
class имя_класса
{
friend тип имя_другого_класса::
имя_функции(список параметров);
friend тип имя_функции(список параметров);
}
Слайд 17Дружественные функции, не являясь элементами класса, имеют доступ к его
закрытым и защищенным элементам. Для того чтобы объявить функцию дружественной
некоторому классу, нужно в этот класс включить ее прототип, перед которым поставить ключевое слово friend.
Дружественная классу функция может быть методом другого класса, или не принадлежать ни к какому классу.
Можно объявить целый класс дружественным данному классу, включив в определение этого класса описание другого класса с ключевым словом friend. Тогда всем элементам дружественного класса будет разрешен доступ ко всем элементам того класса, для которого он объявлен дружественным.
Так как дружественная функция не является членом того класса, для которого она дружественна, то ее нельзя вызвать, используя имя объекта.
Доступ дружественной функции к закрытым элементам класса можно осуществить только через объект класса, который объявлен у нее внутри или передан ей.
Слайд 18
#include
#include
using namespace std;
class Distance // Класс английских мер длины
{ private:
int feet; // футы
float inches; // дюймы
public:
Distance():feet(0),inches(0.0){}
Distance(int ft,float in):feet(ft),inches(in){}
void getdist(); void showdist();
//сложение двух длин
friend Distance add_dist(Distance d1,Distance d2);
};
Пример. Использование дружественной функции
Слайд 19void Distance::getdist()
{cout > feet;
cout > inches;}
void Distance::showdist()
{ cout
<< feet << "\'-" << inches << '\"'; }
Distance add_dist(Distance d1, Distance d2)
{Distance t;
t.inches = d1.inches + d2.inches;//сложение дюймов
t.feet = 0; //с возможным заемом
if (t.inches >= 12.0)
{ t.inches -= 12.0; // уменьшаем дюймы на 12
t.feet++; } // и увеличиваем футы на 1
t.feet += d1.feet + d2.feet;// сложение футов
return t;
}
Слайд 20
int main()
{ Distance dist1, dist3;
dist1.getdist();
Distance
dist2(11,6.25);
dist3=add_dist(dist1, dist2);
cout
dist1.showdist();cout << endl;
cout << "dist2 = ";
dist2.showdist();cout << endl;
cout << "dist3 = ";
dist3.showdist();cout << endl;
getch();
}
Слайд 21
Перегрузка операций
Синтаксис описания операторной функции:
тип operator #
(список параметров)
{ выполняемые действия } ,
# - знак перегружаемой операции.
Операции: . , :: , ?: , sizeof - перегружать нельзя;
Слайд 22Язык С++ располагает рядом встроенных типов данных, включая int, float,
char и т. д. Для работы с данными этих типов
используются встроенные операции, например, сложение (+) и умножение (*). Кроме того, язык С++ позволяет добавлять и перегружать подобные операции для работы с объектами пользовательских классов.
Для перегрузки операции создается операторная функция.
Операторная функция может быть методом класса или дружественной функцией.
Существует несколько ограничений на перегрузку операций:
Операции: . , :: , ?: , sizeof - перегружать нельзя;
Нельзя изменить приоритет операций;
Нельзя менять число операндов операции;
Нельзя создавать новые операции, можно перегружать только существующие;
Операторная функция не может иметь параметров, передаваемых по умолчанию.
Слайд 23
Синтаксис определения операторной функции - метода:
тип имя_класса :
: operator знак операции (описание параметра)
{ операторы,
определяющие действие операции }
Синтаксис определения операторной функции - друга:
friend тип operator знак операции (описание параметров)
{ операторы, определяющие действие операции }
Перегрузка бинарных операций
Слайд 24
Синтаксис определения операторной функции - метода:
тип имя_класса :
: operator знак операции ( )
{ операторы, определяющие
действие операции }
Синтаксис определения операторной функции - друга:
friend тип operator знак операции (описание параметра)
{ операторы, определяющие действие операции }
Перегрузка унарных операций
Слайд 25При перегрузке бинарной операции с использованием операторной функции - метода
ей передается явным образом только один аргумент – объект, располагаемый
с правой стороны от символа операции. Второй же аргумент (объект, располагаемый с левой стороны от символа операции) передается неявно через указатель this.
Тип значения, возвращаемого заданной операцией часто совпадает с именем класса, для которого перегружается данная операция – это облегчает использование перегруженной операции в составных выражениях.
Функции – «друзья» не имеют указателя this, поэтому, если для перегрузки бинарной операции используется функция – «друг» ей явным образом передаются два операнда.
Среди всех операций особое место в контексте перегрузки занимает операция присваивания. В отличие от всех остальных операций, если для класса нет явного ее определения, то она автоматически генерируется компилятором. Такая по умолчанию созданная операция присваивания производит битовую копию объекта. Если для объектов недостаточно простого побитового копирования (например, для объектов, содержащих динамические поля), то необходимо явно перегрузить операцию присваивания.
При перегрузке унарной операции с помощью операторной функции - метода - ни один объект не передается явным образом. Операция же выполняется над объектом, который генерирует вызов этой функции через неявно переданный указатель this.
При перегрузке унарной операции с помощью операторной функции – «друга» - операторной функции передается один операнд.
Слайд 26
#include
#include
using namespace std;
class Distance // Класс английских мер длины
{ private:
int feet; // футы
float inches; // дюймы
public:
Distance():feet(0),inches(0.0){}
Distance(int ft,float in):feet(ft),inches(in){}
void getdist(); void showdist();
//сложение двух длин
Distance operator+(Distance d2);
};
Пример перегрузки бинарной операции +
(с использованием операторной функции-метода)
Слайд 27
void Distance::getdist()
{cout >
feet;
cout > inches;
}
void Distance::showdist()
{
cout << feet << "\'-" << inches << '\"'; }
Distance Distance::operator+(Distance d2)
{ int f = feet + d2.feet; // складываем футы
float i = inches + d2.inches;//складываем
// дюймы
if (i >= 12.0)
{ i-=12.0; // уменьшаем дюймы на 12
f++; } // и увеличиваем футы на 1
return Distance(f, i);//создаем и возвращаем
// временный объект
}
Слайд 28
int main ()
{ Distance dist1, dist3;
dist1.getdist();
Distance dist2(11,6.25);
dist3 = dist1+ dist2;
cout
dist1.showdist();cout << endl;
cout << "dist2 = ";
dist2.showdist();cout << endl;
cout << "dist3 = ";
dist3.showdist();cout << endl;
getch();
}
Слайд 29
#include
#include
using namespace std;
class Distance // Класс английских мер длины
{ private:
int feet; // футы
float inches; // дюймы
public:
Distance():feet(0),inches(0.0){}
Distance(int ft,float in):feet(ft),inches(in){}
void getdist(); void showdist();
//сложение двух длин
friend Distance operator+(Distance d1,Distanmce d2);
};
Пример перегрузки бинарной операции +
(с использованием операторной функции-друга)
Слайд 30
void Distance::getdist()
{cout >
feet;
cout > inches;
}
void Distance::showdist()
{
cout << feet << "\'-" << inches << '\"'; }
Distance operator+(Distance d1,Distance d2)
{int f = d1.feet +d2.feet; // складываем футы
float i = d1.inches +d2.inches; // складываем
// дюймы
if (i >= 12.0)
{ i-=12.0; // уменьшаем дюймы на 12
f++; } // и увеличиваем футы на 1
return Distance(f, i); //создаем и возвращаем
// временный объект
}
Слайд 31
int main ()
{ Distance dist1, dist3;
dist1.getdist();
Distance dist2(11,6.25);
dist3 = dist1+ dist2;
cout
dist1.showdist();cout << endl;
cout << "dist2 = ";
dist2.showdist();cout << endl;
cout << "dist3 = ";
dist3.showdist();cout << endl;
getch();
}
Слайд 32Пример перегрузки унарной операции ++
#include
class Point
{ int
x, y; //2-мерные координаты
public:
Point() { x
= у = 0; }
Point(int i, int j) {x = i; у = j; }
Point operator++(); //префиксная форма "++"
Point operator++ (int); //постфиксная форма "++"
void show () ;
};
// перегрузка префиксной версии операции " ++ "
Point Point ::operator++()
{ x++; y++; // увеличиваем координаты х, у
return *this;} // возвращаем измененное
// значение объекта
Слайд 33// перегрузка постфиксной версии операции " ++ "
Point Point
::operator++(int)
{ temp = *this;// сохраняем исходное значение
x++; y++; // увеличиваем координаты х, у
return temp; // возвращаем исходное
// значение объекта
}
int main ()
{ Point a(l, 2), b;
b = ++a; // Объект b получает значение
// объекта а после ++
b = a++; // Объект b получает значение
// объекта а до ++
...
}
Слайд 34Статические поля и методы класса
Определение и инициализация статического поля:
тип имя_класса : : имя_поля инициализатор;
Пример, int
complex::count = 0;
Обращение к статическому полю через имя объекта:
имя_объекта.имя_статического_поля
Обращение к статическому полю через имя класса:
имя_класса ::имя_статического_поля
Вызов статического метода класса:
имя_класса ::имя_статического_метода
Слайд 35Статические поля и методы объявляются в классе с ключевым словом
static.
Значение статического поля существует в единственном экземпляре для всех объектов.
Доступ к значению статического поля возможен только после его определения и инициализации.
Определение статического поля с инициализацией должно быть размещено в глобальной области после определения класса. Только при определении и инициализации статическое поле класса получает память и становится доступным.
Обращаться к статическому полю класса можно обычным образом через имя объекта. Но к статическим компонентам можно обращаться и тогда, когда объект класса еще не существует. Доступ к статическим компонентам возможен не только через имя объекта, но и через имя класса. Обращаться к статическим полям через имя класса возможно только в том случае, если они объявлены в классе в секции public.
Для обращения к private статическому полю извне можно с помощью статических методов. Эти методы можно вызвать через имя класса.
Слайд 36 Пример:
#include
class TPoint
{ double x,y;
static int N; //количество точек
public:
TPoint(double
x1=0.0,double y1=0.0)
{ N++; x = x1; y = y1;}
static int& count(){return N;}
};
int TPoint::N=0; //инициализация статического поля
void main(void)
{ TPoint A(1.0,2.0); TPoint B(4.0,5.0);
TPoint C(7.0,8.0);
cout << "\nОпределены" << TPoint::count()<<"точки"; }
Слайд 37
Контрольные вопросы
Как организовать передачу объектов в функции и возвращение объекта
из функции?
Друзья класса – понятие, назначение и основные свойства.
Как осуществляется
перегрузка операций в С++. В чем состоят различия между перегрузкой операций с использованием дружественных функций и без них? Какую роль играет указатель this при перегрузке операций?
Статические поля и методы класса: назначение, синтаксис описания и примеры использования