Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Кетков.doc
Скачиваний:
17
Добавлен:
27.09.2019
Размер:
2.22 Mб
Скачать

16.1.2. Вызов конструкторов и деструкторов при наследовании

Последовательность вызова конструкторов и деструкторов легче проследить на следующем примере. В базовом классе B содержится единственный закрытый член данных x, предусмотрены три конструктора (по умолчанию, инициализации и копирования), функция опроса значения закрытого поля и деструктор. Каждый из них выводит свое условное обозначение при вызове. В производном классе D, который наследует поле x в режиме private, содержится и собственное закрытое поле y. В его составе такие же три конструктора, функция опроса значения закрытого поля и деструктор.

Головная программа сначала создает четыре объекта w1, w2, w3 и w4 типа B, а затем четыре объекта q1, q2, q3 и q4 типа D. После создания каждого объекта фиксируется содержимое соответствующих полей и цепочка вызываемых конструкторов. Перед окончанием программы фиксируется цепочка вызовов деструкторов.

#include <iostream.h>

#include <conio.h>

class B {

int x;

public:

B(){x=0; cout<<"Def_B "<<endl;}

B(int n){x=n; cout<<"Init_B "<<endl;}

B(const B &y){x=y.x; cout<<"Copy_B "<<endl;}

int get_x(){return x;}

~B(){cout<<"Destr_B"<<endl;}

};

class D : public B {

int y;

public:

D(){y=0; cout<<"Def_D "<<endl;}

D(int n){y=n; cout<<"Init_D "<<endl;}

D(const D &z){y=z.y; cout<<"Copy_D "<<endl;}

int get_y(){return y;}

~D(){cout<<"Destr_D"<<endl;}

};

void main()

{ B w1;

cout<<"w1.x="<<w1.get_x()<<endl;

B w2(2);

cout<<"w2.x="<<w2.get_x()<<endl;

B w3(w2);

cout<<"w3.x="<<w3.get_x()<<endl;

B w4=w1;

cout<<"w4.x="<<w4.get_x()<<endl;

D q1;

cout<<"q1.x="<<q1.get_x()<<' '<<"q1.y="<<q1.get_y()<<endl;

D q2(2);

cout<<"q2.x="<<q2.get_x()<<' '<<"q2.y="<<q2.get_y()<<endl;

D q3(q2);

cout<<"q3.x="<<q3.get_x()<<' '<<"q3.y="<<q3.get_y()<<endl;

D q4=q1;

cout<<"q4.x="<<q4.get_x()<<' '<<"q4.y="<<q4.get_y()<<endl;

}

//=== Результаты работы ===

Def_B //конструктор B по умолчанию для создания w1.x

w1.x=0 //значение созданного объекта

Init_B //конструктор B инициализации для создания w2.x

w2.x=2 //значение созданного объекта

Copy_B //конструктор B копирования для создания w3.x

w3.x=2 //значение созданного объекта

Copy_B //конструктор B копирования для создания w4.x

w4.x=0 //значение созданного объекта

Def_B //неявный вызов конструктора B для создания q1.x

Def_D //конструктор D по умолчанию для создания q1.y

q1.x=0 q1.y=0 //значения созданных объектов

Def_B //неявный вызов конструктора B для создания q2.x

Init_D //конструктор D инициализации для создания q2.y

q2.x=0 q2.y=2 //значения созданных объектов

Def_B //неявный вызов конструктора B для создания q3.x

Copy_D //конструктор D копирования для создания w3.y

q3.x=0 q3.y=2 //значения созданных объектов

Def_B //неявный вызов конструктора B для создания q4.x

Copy_D //конструктор D копирования для создания w4.y

q4.x=0 q4.y=0 //значения созданных объектов

Destr_D //деструктор D для уничтожения w4.y

Destr_B //деструктор B для уничтожения w4.x

Destr_D //деструктор D для уничтожения w3.y

Destr_B //деструктор B для уничтожения w3.x

Destr_D //деструктор D для уничтожения w2.y

Destr_B //деструктор B для уничтожения w2.x

Destr_D //деструктор D для уничтожения w1.y

Destr_B //деструктор B для уничтожения w1.x

Destr_B //деструктор B для уничтожения q4.x

Destr_B //деструктор B для уничтожения q3.x

Destr_B //деструктор B для уничтожения q2.x

Destr_B //деструктор B для уничтожения q1.x

Обратите внимание на то, что в этом примере создание объектов производного класса начинается с автоматического вызова конструктора базового класса по умолчанию. Кроме того, объекты уничтожаются в порядке, обратном последовательности их создания – самый первый объект разрушается последним.

Однако возможна ситуация, когда ни программист, ни система не включили в базовый класс конструктор по умолчанию. Это происходит в тех случаях, когда программист написал только конструкторы с параметрами. В такой ситуации конструкторы производного класса должны сами позаботиться об инициализации объектов родительского класса. Сделать это можно разными способами – явно вызвать конструктор базового класса либо в своем списке инициализации, либо в теле конструктора. Для защищенных (protected) полей базового класса можно воспользоваться указателем this. В приводимом ниже примере демонстрируются эти возможности. В качестве базового класса выступает класс Point2D, моделирующий точку на плоскости:

class Point2D {

int x,y; //закрытые данные класса Point2D

public:

Point2D(int xx,int yy):x(xx),y(yy){} //конструктор инициализации

Point2D(const Point2D &P):x(P.x),y(P.y){} //конструктор копирования

int get_x(){return x;}

int get_y(){return y;}

};

Порожденный класс Point3D моделирует точку в трехмерном пространстве:

class Point3D: public Point2D {

int z; //новая координата в классе Point3D

public:

Point3D(int xx,int yy,int zz):Point2D(xx,yy),z(zz){}

int get_z(){return z;} //новый метод в классе Point3D

};

А теперь протестируем оба класса на следующей программе:

#include <iostream.h>

#include <conio.h>

void main()

{ Point2D P2(1,2);

Point3D P3(3,4,5);

cout<<"P3.x="<<P3.get_x()<<" P3.y="<<P3.get_y()<<" P3.z=" <<P3.get_z()<<endl;

cout<<"P2.x="<<P2.get_x()<<" P2.y="<<P2.get_y()<<endl;

P2=P3;

cout<<"P2.x="<<P2.get_x()<<" P2.y="<<P2.get_y()<<endl;

getch();

}

//=== Результат работы ===

P3.x=3 P3.y=4 P3.z=5

P2.x=1 P2.y=2

P2.x=3 P2.y=4

Производному классу по наследству достались приватные данные – координаты (x,y) родительского объекта и общедоступные методы доступа к этим координатам. Поэтому в головной программе мы можем пользоваться этими методами как по отношению к объектам типа Point2D, так и по отношению к объектам типа Point3D. Немного странным кажется оператор присваивания двухмерному объекту P2 значения трехмерного объекта P3. Но происходит вполне естественная операция – те поля, которые являются общими у этих двух объектов, переносятся, а "лишнее" поле P3.z отсекается. Обратная операция P3=P2 была бы ошибочной, т.к. компилятор не "знает", чем следует заполнить поле P3.z.

Если бы поля (x,y) в базовом классе были объявлены как защищенные (protected), то их инициализацию в конструкторе производного класса можно было бы выполнить и так:

Point3D(int xx,int yy,int zz):z(zz)

{ this->x=xx; this->y=yy; }