- •Лекции по Объектно-ориентированному программированию.
- •Классы.
- •Квалификаторы доступа.
- •Инициализация полей данных.
- •Операторы new и delete.
- •Перегруженные функции.
- •Конструктор копирования.
- •Статические компоненты класса.
- •Дружественные функции.
- •Перегрузка операций.
- •Наследование.
- •Производные классы. Доступ к базовым классам.
- •Возможны следующие варианты доступа:
- •Конструкторы и деструкторы в иерархии классов.
- •Виртуальные функции.
- •Virtual тип_возвр_знач-я имя_функции (параметры)
- •Потоки ввода / вывода.
- •Операции помещения и извлечения.
Конструктор копирования.
При создании класса как абстрактного типа данных, нам нет необходимости специально инициализировать какую-либо переменную абстрактного типа. Надо просто присвоить вновь создаваемому объекту значение уже имеющегося. Для этого используется конструктор специального вида, который называется “Конструктором копирования”.
Сlass TObject {---- // ----
---- // ----
public : TObject (TObject & );
---- // ----
}; либо
Сlass TObject {... ... ...
... ... ...
public : TObject (const TObject & (тип));
... ... ...
};
Class Tdefine {private : int TT;
public :
Tdefine (int r =1) {TT = r ;}
Tdefine (const Tdefine &NewObject)
{TT =NewObject . TT;}
void show_all ( )
{cout << ”значение ТТ = ” << TT <<endl;}
~ Tdefine ( ) { }
}; // конец определения класса
main ( )
{Tdefine copia1;
Tdefine copia2 (3 );
Tdefine copia3 = copia2; |
copia1. show_all ( ); // 1
copia2. ------ // ------ // 3
copia3. ------ // ------ // 3
Выделенная строка означает не только обычное копирование. Знак “ = ” между объектами абстрактного типа означает предписание компилятору использовать конструктор копирования. Во многих компиляторах часто происходит автоматическая генерация этого конструктора, если он не был описан программистом.
Деструкторы – это функции – члены класса, которые обеспечивают действия, обратные действиям конструктора.
Как правило, деструктор содержит оператор освобождения динамической памяти
~ TObject ( )
{delete name;}
Иначе память будет считаться распределенной, и к ней нельзя будет обращаться.
Статические компоненты класса.
Есть возможность доступа всех созданных объектов конкретного класса к одной переменной (члену данному), содержимое которой хранится в одном месте. Для этого объявляют переменной:
static тип имя
Такой класс памяти может использоваться не только для объявления статических членов данных (переменных класса), но и для функций-методов класса. Память для этого резервируется при запуске программы до явного создания объекта. Поэтому он является как бы единым для всех копий членов данных. Доступ для такой переменной (::) возможен только после инициализации.
тип имя_класса :: имя_переменной = нач.значение;
Нельзя инициализировать статические члены класса внутри класса, в теле членов функций-методов класса. Их инициализация возможна в области видимости файла. В этом отношении статические члены данных похожи на глобальные переменные.
class Stat
{public : static int flag;
Stat ( ) {flag ++;}
int Ret_flg {return flag;}
};
main ( )
{stat S1, S2;
cout << ”количество вызовов конструктора : ”<<Stat :: flag <<endl;
stat S3;
cout << ”количество вызовов ... : ”<<S3. Ret_flg ( ); // 3
};
-
Указатель this.
-
Указатель на методы класса.
1. Каждый новый экземпляр класса получает свою собственную копию данных.
Функция – метод определяет, к какому объекту относится конкретный компонент класса.
# include <iostream.h>
# include <string.h>
class Stroka
{char *ss;
public : stroka (char*text) {ss =text;}
void write_ss ( ) {cout <<”string : “<<ss;endl;}
};
void main ( )
{stroka s1 (“Текст1”);
stroka s2 (“Текст2”);
s1.write_ss ( ); // Текст 1
s2.write_ss ( ); } // Текст 2
Для решения этой проблемы вводится неявный указатель this на объект класса. Функция write_ss ( ) в качестве неявного аргумента получает адрес одного из объектов s1 и s2.
Этот указатель (this) определен в каждой нестатической функции класса.
Надо проинициализировать:
имя_класса *this = адрес_объекта или
имя_класса *сonst this = адрес_объекта
Изменим наш пример.
public : ------ // ------ // ------
void write_ss ( ) {cout <<”string : “<<ss <<endl;}
void write_ss ( ) {cout <<”string : “<<this -> ss <<endl;}
Рассмотрим другой пример, когда имя переменной члена класса совпадает с именем функции.
Снимаем неоднозначность.
# include ------ // ------
# include ------ // ------
class Stroka
{char *name;
char *ss;
public : stroka (char *ss, char *text)
{name =ss; // неоднозначность присваивания полю name
ss = text; // кому присв. значение ?
void wr_ss ( )
{cout <<”string eto”<<name <<endl;
cout <<”string eto : ”<<ss <<endl;}
};
main ( )
{stroka s1 (“Первая строка”, ”text 1”);
stroka s2 (“Вторая строка”, ”text 2”);
s1.wr_ss ( );
s2.wr_ss ( );
}
*
# include
# include
class stroka
{char *name;
char *ss;
public : stroka (char *ss, char *text)
this → name = ss; // полю данных name присваивается значение конструктора stroka.
this → ss = text; // полю данных ss ------ // ------------ // ------------ // ------------ // ------------ // ------.
Можно использовать операцию расширения области видимости (::)
stroka :: name = ss;
stroka :: ss = text;
Использование this наиболее эффективно в функциях, которые явно работают с указателями на объект, для которых были вызваны эти функции.
2. Указатель на функции (методы класса).
Так как класс – новый тип данных, то для классов и для объектов типа “класс” может быть также определена операция получения адреса. То есть в программе мы можем использовать указатели на компоненты класса.
# include ------ // ------
------ // ------// ------
class UK {public : int u1, u2;
UK (int value 1 =Ø, int value 2 = Ø)
{u1 = value 1;
u2 = value 2;
void print ( )
{cout << ”znachenie polya data = “ <<endl;
cout << “u1 = “ << u1 <<endl;
cout << “u2 = “ << u2 <<endl; }
}; // конец определения класса.
void set (UK& obj, int value)
{int UK :: * pos = &UK :: u2;
obj. *pos = value;} // pos- переменная, которая указывает на поле данных класса u2 типа int.
void main ( )
{UK ob1(1Ø); // присваиваем значение только u1, u2 =0 (cм. конструктор)
ob1. print ( );
set (ob1, 3Ø);
ob1. print ( );
}
Пример.
# include ------ // ------
------ // ------// ------
class UK {------// ------
------// -----// ------ }
/ * void set ( ) */
void main ( )
{UK ob2 (1Ø, 3ØØ);
void (UK1 :: *func) ( ) = & UK :: print ( ); // объявление указателя на функцию-метод класса.
тип (*имя_указателя) ( ) = & имя_функции //
(ob2. *func) ( ); // косвенный вызов print
Указатели на методы класса отличаются от указателей на обычные функции только явным присутствием в их объявлении имени класса, за которым следует операция расширения области видимости ( :: ). В остальном отличий нет.