- •Обзорные лекции по курсу
- •1.Концепции и методология объектно-ориентированного программирования
- •2.Классы. Конструкторы и деструкторы
- •Void set_a(int num);
- •Void AnyClass::get_a()
- •Int AnyClass::set_a(int num)
- •Конструкторы и деструкторы. Список инициализации элементов
- •Void Show();
- •Int main()
- •Void Show();
- •Int main()
- •Статические члены класса
- •3. Простое и множественное наследование
- •Int GetX(){return X;}
- •Int GetY(){return у;}
- •Int GetX(){return X;}
- •Int GetY(){return y;}
- •Void ShowX(){cout « GetX() « ' ' ;}
- •Void ShowY(){cout « GetY()« ' ';}
- •OutCoord(int _x, int _y): Coord(_x, _y){}
- •Int GetX() {return X;}
- •Int GetY(){return y;}
- •Void ShowX(){cout « GetX() « ' ';}
- •Void ShowY(){cout « GetY()« ' ';}
- •Void ShowX(){cout « Coord::GetX() « ' ';}
- •PrintMsg(int _x, int _y, char* msg): Coord(_x, _y), SaveMsg(msg){}
- •Void set_a(int num);
- •Void AnyClass::get_a()
- •Int AnyClass::set_a(int num)
- •Void Show();
- •Int main()
- •Int GetX() {return X;}
- •PrintMsg(int _x, int _y, char* msg): Coord(_x, _y), SaveMsg(msg){}
- •Упражнения для самопроверки
Int GetX(){return X;}
Int GetY(){return y;}
void SetX(int _x){x = _x;}
void SetY(int _y){y = _y;}
};
class OutCoord: public Coord
{
public:
OutCoord(int _x, int _y): Coord(_x, _y){}
Void ShowX(){cout « GetX() « ' ' ;}
Void ShowY(){cout « GetY()« ' ';}
};
main ()
{
OutCoord* ptr;
ptr = new OutCoord(10,20); //Создание объекта a кучe
ptr->ShowX();
ptr->ShowY();
cout « "\n";
delete ptr; //Удаление объекта из кучи
return 0;
}
Здесь класс OutCoord является производным от базового класса Coord. К членам класса Coord он добавляет две открытые функции и конструктор. Напомним, что конструкторы не наследуются. Поэтому производный класс либо должен объявить свой конструктор, либо предоставить возможность компилятору сгенерировать конструктор по умолчанию. Правила, действующие в отношении конструкторов по умолчанию, были нами изложены выше.
Рассмотрим теперь, как строится конструктор производного класса, предоставляемый программистом. Поскольку производный класс должен унаследовать все члены родительского, при построении объекта своего класса он должен обеспечить инициализацию унаследованных данных-членов, причем она должна быть выполнена до инициализации данных-членов производного класса, так как последние могут использовать значения первых. В связи с этим для построения конструктора производного класса применяется следующая конструкция:
<констр_произв_класса> (<список параметров>):
<констр_базового_класса> (<cписок_apг>)
{<тело_конструктора>)
То есть используется список инициализации элементов, в котором указывается конструктор базового класса. Часть параметров, переданных конструктору производного класса, обычно используется в качестве аргументов конструктора базового класса. Затем в теле конструктора производного класса выполняется инициализация данных-членов, принадлежащих собственно этому классу.
В приведенном выше примере эта конструкция использована для создания конструктора класса OutCord. В данном случае конструктор имеет вид:
OutCoord(int _x, int _y): Coord(_x, _y){}
Тело конструктора оставлено пустым, так как класс OutCoord не имеет собственных данных-членов. Все параметры конструктора производного класса просто переданы конструктору базового класса для осуществления инициализации. Аргументы конструктору производного класса передаются в операторе new, который неявно вызывает этот конструктор. Затем программа вызывает функции-члены ShowX () и ShowY () объекта, являющегося представителем производного класса, которые выводят на экран в одной строке заданные значения координат. Наконец, оператор delete вызывает удаление объекта, для чего он неявно вызывает деструктор производного класса.
Если предоставляемый программистом конструктор не имеет параметров, то есть является конструктором по умолчанию, то при создании экземпляра производного класса автоматически вызывается конструктор базового класса. После того как объект создан, конструктор базового класса становится недоступным.
Хотя конструктор базового класса и наследуется, вызывается он только компилятором, когда конструируется объект производного класса. Конструктор, в отличие от других унаследованных функций, вызвать явно нельзя.
В отношении деструкторов производных классов также действуют определенные правила. Деструктор производного класса должен выполняться раньше деструктора базового класса (иначе деструктор базового класса мог бы разрушить данные-члены, которые используются и в производном классе). Когда деструктор производного класса выполнит свою часть работы по уничтожению объекта, вызывается деструктор базового класса. Причем вся работа по организации соответствующего вызова возлагается на компиллятор, программист не должен заботиться об этом.
Чтобы более наглядно изучить работу конструкторов и деструкторов базового и производного классов, рассмотрим следующий пример:
#include <iostream.h>
class Base
{
public:
Base()
{
cout « "Мы в конструкторе "« "базового класса" « "\n";
}
~Base ()
{
cout « "Мы в деструкторе базового” « "класса" « "\n";
}
};
class Derived: public Base
{
public:
Derived()
{
cout « "Мы в конструкторе производного класса" « "\n";
}
~Derived ()
{
cout «"Мы в деструкторе производного класса" « "\n";
}
};
main()
{
Derived ob;
return 0;
}
Эта программа выводит на экран следующее:
Мы в конструкторе базового класса
Мы в конструкторе производного класса
Мы в деструкторе производного класса
Мы в деструкторе базового класса
Для дальнейшего очень важно понимать работу конструкторов и деструкторов при наследовании.
В производном классе обычно добавляются новые члены к членам базового класса. Однако существует также возможность переопределения (или замещения) членов базового класса. Обычно используется переопределение функций-членов базового класса. Чтобы переопределить функцию-член базового класса в производном классе, достаточно включить ее прототип в объявление этого класса и затем дать ее определение. Конечно, прототипы переопределяемой функции в базовом и производном классах должны совпадать.
Модифицируем рассмотренный нами пример так, чтобы производный класс переопределял функции-члены своего базового класса GetX() nGetY():
#include <iostream.h>
class Coord
{
protected:
int x, y;
public:
Coord(int _x, int _y) {x = _x; у = _y;}
Coord() {x = 0; у = 0;}