Возник вопрос по теме множественного наследования в C++. Допустим, у нас есть некоторый код, представляющий собой случай «ромбовидного» наследования, в котором класс A является виртуальным базовым классом для классов B и С, которые, в свою очередь, являются базовыми для класса D
#include
class A {
public:
int a;
public:
A() : a(0) {}
A(int _a) : a(_a) {}
};
class B : virtual public A {
public:
int b;
public:
B(int _a, int _b) : A(_a), b(_b) {}
};
class C : virtual public A {
public:
int c;
public:
C(int _a, int _c) : A(_a), c(_c) {}
};
class D : public B, public C {
public:
int d;
public:
D(int _a, int _b, int _c, int _d) : B(_a, _b), C(_a, _c), d(_d) {}
};
int main() {
D obj(1, 2, 3, 4);
std::cout << "obj.a = " << obj.a << std::endl; // Выводит: 0
std::cout << "obj.b = " << obj.b << std::endl; // Выводит: 2
std::cout << "obj.c = " << obj.c << std::endl; // Выводит: 3
std::cout << "obj.d = " << obj.d << std::endl; // Выводит: 4
return 0;
}
Ясно, что наличие виртуального базового класса А гарантирует, что при конструировании объекта типа D мы будем иметь только одну копию подобъекта класса А. Это приводит к совсем другому поведению, отличному от того, если бы базовый класс был не виртуальным. По результатам выполненного кода видно, что за создание этого единственного экземпляра подобъета класса А, отвечает конструктор по-умолчанию в классе A, причем его вызов происходит в конструкторе класса D посредством списка инициализации, а не в непосредсвенных потомках B и С, которые напрямую наследуют от А. Ясно, что вместо генерирования компилятором вывоза конструктора по-умолчанию можно явно указать вызов любого другого конструктора класса A в списке инициализации конструктора класса D.
Вопрос в следующем: вызывая конструкторы B(_a, _b) и C(_a, _c) непосредственных классов-предков класса D, мы должны по цепочке прийти к явному вызову конструктора класса A в списках инициализации класса B и класса C, но этого не происходит при наличии виртуального базового класса. Получается код инициализации A(_a) в конструкторах B и C классов просто игнорируется компилятором и его можно безболезненно убрать, не меняя поведения?
Ответ
Убрать можно, на насколько это будет безболезненно в вашем абстрактном случае сказать не реально.
И к слову говоря, у вас класс A не является виртуальным, так как в нём нет виртуальных членов функций и указателя _vptr на vtable и т.д. и т.п.
По причине виртуального наследования классов B и C от A, класс D напрямую наследует A, а не через B и С для избежания одинаковых копий объекта А в D - поэтому если явно вызвать параметризированный конструктор А в D у вас потом выведется 1, 2, 3, 4.
Тоесть виртуальное наследование гарантирует прямое наследование без промежуточных предков, что в свою очередь обеспечивает только одну копию предка в наследнике.
Комментариев нет:
Отправить комментарий