#cpp
Возник вопрос по теме множественного наследования в C++. Допустим, у нас есть некоторый код, представляющий собой случай «ромбовидного» наследования, в котором класс A является виртуальным базовым классом для классов B и С, которые, в свою очередь, являются базовыми для класса D: #includeclass 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 классов просто игнорируется компилятором и его можно безболезненно убрать, не меняя поведения?
Ответы
Ответ 1
Убрать можно, на насколько это будет безболезненно в вашем абстрактном случае сказать не реально. И к слову говоря, у вас класс A не является виртуальным, так как в нём нет виртуальных членов функций и указателя _vptr на vtable и т.д. и т.п. По причине виртуального наследования классов B и C от A, класс D напрямую наследует A, а не через B и С для избежания одинаковых копий объекта А в D - поэтому если явно вызвать параметризированный конструктор А в D у вас потом выведется 1, 2, 3, 4. Тоесть виртуальное наследование гарантирует прямое наследование без промежуточных предков, что в свою очередь обеспечивает только одну копию предка в наследнике.
Комментариев нет:
Отправить комментарий