Если порожденный класс наследует элементы одного базового класса, то такое наследование называется одиночным. Однако, возможно и множественное наследование. Множественное наследование позволяет порожденному классу наследовать элементы более, чем от одного базового класса. Синтаксис заголовков классов расширяется так, чтобы разрешить создание списка базовых классов и обозначения их уровня доступа:
{...};
class Y
{...};
class Z
{...};
class A : public X, public Y, public Z
{...};
Класс А обобщенно наследует элементы всех трех основных классов.

Для доступа к членам порожденного класса, унаследованного от нескольких базовых классов, используются те же правила, что и при порождении из одного базового класса. Проблемы могут возникнуть в следующих случаях:
- если в порожденном классе используется член с таким же именем, как в одном из базовых классов;
- когда в нескольких базовых классах определены члены с одинаковыми именами.
В этих случаях необходимо использовать оператор разрешения контекста для уточнения элемента, к которому осуществляется доступ, именем базового класса.
Так как объект порожденного класса состоит из нескольких частей, унаследованных от базовых классов, то конструктор порожденного класса должен обеспечивать инициализацию всех наследуемых частей. В списке инициализации в заголовке конструктора порожденного класса должны быть перечислены конструкторы всех базовых классов. Порядок выполнения конструкторов при порождении из нескольких классов следующий:
- конструкторы базовых классов в порядке их задания;
- конструкторы членов, являющихся объектами класса;
- конструктор порожденного класса.
Деструкторы вызываются в порядке обратном вызову конструкторов.
Пример
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
using namespace std;
class X
{
protected:
int key;
public:
X(int i = 0) { cout << "X constructor" << endl; key = i; };
~X() { cout << "X destroyed" << endl; cin.get(); };
};
class Y
{
protected:
int key;
public:
Y(int i = 0) { cout << "Y constructor" << endl; key = i; };
~Y() { cout << "Y destroyed" << endl; cin.get(); };
};
class Z
{
protected:
int key;
public:
Z(int i = 0) { cout << "Z constructor" << endl; key = i; };
~Z() { cout << "Z destroyed" << endl; cin.get(); };
};
class A : public X, public Y, public Z
{
int key;
public:
A(int i = 0) : X(i + 1), Y(i + 2), Z(i + 3)
{
key = X::key + Y::key + Z::key;
}
int getkey(void) { return(key); }
};
int main()
{
A object(4);
cout << "object.key = " << object.getkey();
cin.get();
return 0;
}
Результат выполнения

Виртуальные базовые классы
Базовый класс может быть задан только один раз в списке порождения нового класса. Однако базовый класс может встретиться несколько раз в иерархии порождения.
Такая иерархия порождения несет двусмысленность при доступе к наследуемым членам класса X и может привести к ошибкам. В этом случае класс X будет дважды присутствовать в A. Хорошо это или плохо - зависит от решаемой задачи.
Если двойное вхождение объектов класса X в объект класса А не является допустимым, существует два выхода для разрешения такой ситуации:
- преобразование порождения из нескольких классов в порождение из одного класса и объявление дружественных классов.
class Y : public X
{friend class Z; }; // класс Z имеет доступ к Y - изменение механизма наследования таким образом, чтобы вне зависимости от того, как часто базовый класс будет встречаться в иерархии порождения, генерировалась бы только одна его копия. Такой механизм заключается в возможности создания виртуальных базовых классов.
Базовый класс определяется как виртуальный заданием ключевого слова virtual в списке порождения перед именем базового класса или указанием типа наследования
{
// тело_класса
};
Если базовый класс объявляется как виртуальный при порождении нового класса, то каждый объект будет содержать свою собственную часть, а вместо базовой части будет содержать указатель на виртуальный базовый класс.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
using namespace std;
class X
{
protected:
int key;
public:
X(int i = 0) { cout << "X constructor" << endl; key = i; };
};
class Y : virtual public X
{
public:
Y(int i = 0) { cout << "Y constructor" << endl; key = i; };
};
class Z : virtual public X
{
public:
Z(int i = 0) { cout << "Y constructor" << endl; key = i; };
};
class A : public Y, public Z {
int key;
public:
A(int i = 0) : Y(i + 2), Z(i + 3)
{
key = Y::key + Z::key;
}
int getkey(void) { return(key); }
};
int main() {
A object(4);
cout << "object.key = " << object.getkey();
cin.get();
return 0;
}
Результат выполнения

Как видим, объект класса X был создан только один раз. Значение поля key изменяется каждый раз при доступе к нему как через класс Y, так и через класс Z.
Если в строках 11 и 17 этого примера убрать слово virtual, то результат работы программы изменится следующим образом:

То есть в этом случае и объект класса Y, и объект класса Z, которые наследует объект класса A, будут содержать свою собственную копию объекта класса X.
Конструкторы и деструкторы при использовании виртуальных базовых классов выполняются в следующем порядке:
- конструкторы виртуальных базовых классов выполняются до конструкторов не виртуальных базовых классов, независимо от того, как эти классы заданы в списке порождения;
- если класс имеет несколько виртуальных базовых классов, то конструкторы этих классов вызываются в порядке объявления виртуальных базовых классов в списке порождения;
- деструкторы виртуальных базовых классов выполняются после деструкторов не виртуальных базовых классов.
При порождении с использованием виртуальных базовых классов сохраняются те же правила видимости, что и при порождении с не виртуальными классами.
Назад: Язык C++
Комментариев к записи: 2