При использовании производных классов важно представлять себе, каким образом и когда исполняются конструкторы и деструкторы базового и производного классов. Начнем рассмотрение с конструкторов.
Как базовый класс, так и производный класс могут иметь конструкторы. (В многоуровневой иерархии классов каждый из классов может иметь конструкторы, но мы начнем с наиболее простого случая.) Когда базовый класс имеет конструктор, этот конструктор исполняется перед конструктором производного класса. Например, рассмотрим следующую короткую программу:
#include <iostream.h>
class Base
{
public:
Base() { cout << "\nBase created\n"; )
};
class D1() : public Base
{
public:
D1() { cout << "D1 created\n"; }
};
int main()
{
D1 d1;
// ничего не делается, но выполняются конструкторы
return 0;
}
Эта программа создает объект класса D1. Она выводит на экран следующий текст:
Base created
D1 created
Здесь d1 является объектом класса D1, производным класса Base. Таким образом, при создании объекта d1 сначала вызывается конструктор Base(), а затем вызывается конструктор D1().
Конструкторы вызываются в том же самом порядке, в каком классы следуют один за другим в иерархии классов. Поскольку базовый класс ничего не знает про свои производные классы, то его инициализация может быть отделена от инициализации производных классов и производится до их создания, так что конструктор базового класса вызывается перед вызовом конструктора производного класса.
В противоположность этому деструктор производного класса вызывается перед деструктором базового класса, поскольку уничтожение объектов должно выполняться в порядке, обратном порядку их создания. Следующая программа иллюстрирует порядок, в котором выполняются конструкторы и деструкторы:
#include <iostream.h>
class Base
{
public:
Base() { cout << "\nBase created\n"; }
~Base() { cout << "Base destroyed\n\n"; }
};
class D1() : public Base
{
public:
D1() { cout << "D1 created\n; }
~D1() { cout << "D1 destroyed\n"; }
};
int main()
{
D1 d1;
return 0;
}
Эта программа выдаст следующий текст на экран:
Base created
D1 created
D1 destroyed
Base destroyed
Производный класс может служить базовым классом для создания следующего производного класса. Когда такое происходит, конструкторы исполняются в порядке наследования, а деструкторы — в обратном порядке. В качестве примера рассмотрим следующую программу, использующую класс D2, производный от класса D1:
#include <iostream.h>
class Base
{
public:
Base() { cout << "\nBase created\n"; }
~Base() { cout << "Base destroyed\n"; }
};
class D1() : public Base
{
public:
D1() { cout << "D1 created\n"; }
~D1() { cout << "D1 destroyed\n"; }
};
class D2: public D1
{
public:
D2() { cout << "D2 created\n"; }
~D2() { cout << "D2 destroyed\n"; }
};
int main()
{
D1 d1;
D2 d2;
return 0;
}
Эта программа выдаст следующий результат:
Base created
D1 created
Base created
D1 created
D2 created
D2 destroyed
D1 destroyed
Base destroyed
D1 destroyed
Base destroyed
ПАМЯТКА: Следует запомнить, что конструкторы вызываются в том порядке, в котором классы выводились один из другого. Порядок вызова деструкторов обратный.
Один класс может наследовать атрибуты двух и более классов одновременно. Для этого используется список базовых классов, в котором каждый из базовых классов отделен от других запятой. Общая форма множественного наследования имеет вид:
class имя_порожденного_класса: список базовых классов
{
};
В следующем примере класс Z наследует оба класса X и Y:
#include <iostream.h>
class X
{
protected:
int a;
public:
void set_a(int i) { a = i; }
};
class Y
{
protected:
int b;
public:
void set_b(int i) { b = i; }
};
// Z наследует как от X, так и от Y
class Z: public X, public Y
{
public:
int make_ab() { return a*b; }
};
int main()
{
Z i;
i.set_a(10);
i.set_b(12);
cout << i.make_ab();
return 0;
}
Поскольку класс Z наследует оба класса X и Y, то он имеет доступ к публичным и защищенным членам обоих классов X и Y.
В предыдущем примере ни один из классов не содержал конструкторов. Однако ситуация становится более сложной, когда базовый класс содержит конструктор. Например, изменим предыдущий пример таким образом, чтобы классы X, Y и Z содержали конструкторы:
#include <iostream.h>
class X
{
protected:
int a;
public:
X() { a = 10; cout << "Initializing X\n"; }
};
class Y
{
protected:
int b;
public:
Y() { cout << "Initializing Y\n"; b = 20; }
};
// Z наследует как от X, так и от Y
class Z: public X, public Y
{
public:
Z() { cout << "Initializing Z\n"; }
int make_ab() { return a*b; }
};
int main()
{
Z i;
cout << i.make_ab();
return 0;
}
Программа выдаст на экран следующий результат:
Initializing X
Initializing Y
Initializing Z
Обратим внимание, что конструкторы базовых классов вызываются в том порядке, в котором они указаны в списке при объявлении класса Z.
В общем случае, когда используется список базовых классов, их конструкторы вызываются слева направо. Деструкторы вызываются в обратном порядке — справа налево.