// Наследование двух базовых классов.
class derived: public base1, public base2 {
public:
void set(int i, int j) { x = i; у = j; }
};
int main()
{
derived ob;
ob.set (10, 20); // член класса derived
ob.showx(); // функция из класса base1
ob.showy(); // функция из класса base2
return 0;
}
Как видно из этого примера, чтобы обеспечить наследование нескольких базовых классов, необходимо через запятую перечислить их имена в виде списка. При этом нужно указать спецификатор доступа для каждого наследуемого базового класса.
При использовании механизма наследования обычно возникает два важных вопроса, связанных с конструкторами и деструкторами. Первый: когда вызываются конструкторы и деструкторы базового и производного классов? Второй: как можно передать параметры конструктору базового класса? Ответы на эти вопросы изложены в следующем разделе.
Базовый и/или производный класс может содержать конструктор и/или деструктор. Важно понимать порядок, в котором выполняются эти функции при создании объекта производного класса и его (объекта) разрушении.
Рассмотрим короткую программу.
#include
using namespace std;
class base {
public:
base() { cout <<"Создание basе-объекта.\n"; }
~base() { cout <<"Разрушение bаsе-объекта.\n"; }
};
class derived: public base {
public:
derived() { cout <<"Создание derived-объекта.\n"; }
~derived() { cout <<"Разрушение derived-объекта.\n"; }
};
int main()
{
derived ob;
// Никаких действий, кроме создания и разрушения объекта ob.
return 0;
}
Как отмечено в комментариях для функции main(), эта программа лишь создает и тут же разрушает объект
Создание base-объекта.
Создание derived-объекта.
Разрушение derived-объекта.
Разрушение base-объекта.
Судя по результатам, сначала выполняется конструктор класса
Результаты вышеописанного эксперимента можно обобщить следующим образом. При создании объекта производного класса сначала вызывается конструктор базового класса, а за ним — конструктор производного класса. При разрушении объекта производного класса сначала вызывается его "родной" конструктор, а за ним — конструктор базового класса. Другими словами, конструкторы вызываются в порядке происхождения классов, а деструкторы — в обратном порядке.
Вполне логично, что функции конструкторов выполняются в порядке происхождения их классов. Поскольку базовый класс "ничего не знает" ни о каком производном классе, операции по инициализации, которые ему нужно выполнить, не зависят от операций инициализации, выполняемых производным классом, но, возможно, создают предварительные условия для последующей работы. Поэтому конструктор базового класса должен выполняться первым.
Аналогичная логика присутствует и в том, что деструкторы выполняются в порядке, обратном порядку происхождения классов. Поскольку базовый класс лежит в основе производного класса, разрушение базового класса подразумевает разрушение производного. Следовательно, деструктор производного класса имеет смысл вызвать до того, как объект будет полностью разрушен.
При расширенной иерархии классов (т.е. в ситуации, когда производный класс становится базовым классом для еще одного производного) применяется следующее общее правило: конструкторы вызываются в порядке происхождения классов, а деструкторы — в обратном порядке. Например, при выполнении этой программы
#include
using namespace std;
class base {
public:
base() { cout <<"Создание base-объекта.\n"; }
~base(){ cout <<"Разрушение base-объекта.\n"; }
};
class derived1 : public base {
public: