17.2 Множественное наследование
В С++ существует возможность наследовать более одного класса в качестве базового. Такой механизм наследования называется множественным наследованием. При множественном наследовании объекты производных классов могут использовать данные и методы нескольких классов, а также в свою очередь быть базовыми классами для других.
Один класс может наследовать атрибуты двух и более классов одновременно. Для этого используется список базовы х классов, в котором каждый из базовых классов отделен от других запятой.
class имя_порожденного_класса: список_базовых_классов
{
…
}
Последовательность расположения непосредственных базовых классов в списке определяет очередность вызова конструкторов умолчания. Этот порядок влияет и на очередность вызова деструкторов при уничтожении этих объектов.
Листинг 17.1 Пример множественного наследования 1
#include <iostream.h>
class X {
protected:
int a;
public:
void make_a (int i) {a=i;}
};
class Y {
protected:
int b;
public:
void make_b (int i) {b=i;}
};
class Z : public X, public Y {
int make_ab() { return a*b;}
};
int main ()
{
Z i;
i. make_a (10);
i. make_b(12);
cout << i. make_ab ();
return 0;
}
Поскольку класс Z наследует оба класса X и Y, то он имеет доступ к публичным и защищенным членам обоих классов.
Ситуация становится более сложной, когда класс содержит конструктор.
Листинг 17.2 Пример множественного наследования 2
#include <iostream.h>
class X {
protected:
int a;
public:
X () {a=10; cout << “ Initializing X \n”;}
};
class Y {
protected:
int b;
public:
Y () {b=12; cout << “ Initializing Y\n”;}
};
class Z : public X, public Y {
Z() { cout << “ Initializing Z\n”; }
int make_ab() { return a*b;}
};
int main ()
{ Z i;
cout << i. make_ab ();
return 0;
}
Initializing X
InitializingY
Initializing Z
Конструкторы базовых классов вызываются в том порядке, в котором они указаны в списке при объявлении класса Z. Деструкторы вызываются в обратном порядке (справа налево).
Когда базовый класс имеет конструктор с аргументами, производные классы должны явным образом обрабатывать эту ситуацию путем передачи базовому классу необходимых аргументов. Для этого используется, расширенная форма конструкторов производного класса, в которые передаются аргументы конструкторам базового класса.
Порожденный_ конструктор (список_аргументов): базовый1 (список_аргументов), базовый2 (список_аргументов), …, базовыйn (список_аргументов){ …};
Здесь под базовый1, …, базовыйn обозначены имена базовых классов, наследуемые производным классом.
Листинг 17.3 Пример множественного наследования 3
#include <iostream.h>
class X {
protected:
int a;
public:
X (int i) {a=i; }
};
class Y {
protected:
int b;
public:
Y (int i) {b=i; }
};
class Z : public X, public Y {
Z(int x, int y): X(x), Y(y) { cout << “ Initializing Z\n”; }
int make_ab() { return a*b;}
};
int main ()
{
Z i(10,20);
cout << i. make_ab ();
return 0;
}
Атрибуты наследования и их смысл тот же, что и при одиночном наследовании. Данная последовательность базовых классов определяет порядок вызова конструкторов базовых классов. При создании экземпляра производного класса в первую очередь вызовется конструктор базового класса, имя которого определено первым в списке наследуемых классов. Далее - по порядку. Последним вызовется конструктор производного класса. При уничтожении объекта производного класса деструкторы вызываются в обратном порядке вызовам конструкторов. Как и в случае одиночного наследования, если конструктор или конструкторы базового класса содержат как минимум один аргумент, то производный класс также должен содержать конструктор.
Одно и тоже имя класса не может входить боле одного раза в список баз при объявлении производного класса. Это означает, что в наборе непосредственных базовых классов, которые участвуют в формировании производного класса не должно встречаться повторяющихся элементов.