С++, являются многоуровневыми конструкторами, называемыми несколько раз?
Многочисленные наследуемые конструкторы называются несколько раз? И в каком порядке называются конструкторы? Это зависит от порядка в списке наследования?
Вот пример (это только для того, чтобы сделать ситуацию ясной, не реальный пример).
class Base {};
class DerivedBaseOne : public Base {};
class DerivedBaseTwo : public Base {};
class Derived : public DerivedBaseTwo, public DerivedBaseOne
{};
//somewhere in the code, is Base() called two times here?
Derived * foo = new Derived();
Является ли конструктор Base() дважды вызванным? И в каком порядке называются конструкторы? База сначала? Или DerivedBaseOne() или DerivedBaseTwo() сначала?
Спасибо.
Ответы
Ответ 1
Как вы его пишете, Derived
имеет два отдельные подобъекты типа Base
, и каждый получает свой собственный конструктор, вызванный из соответствующего конструктора DerivedBaseXXX
, из которого он является подобъектом. Порядок вызовов следует порядку объявления.
В отличие от вас, объявляйте DerivedBaseXXX : virtual public Base
, тогда существует только один Base
подобъект, а его конструктор вызывается из самого производного объекта, то есть из объекта Derived
.
(Объяснить немного подробнее: A (возможно, однократно наследующий) класс строится сначала: 1) вызывает конструктор базового класса, затем 2) вызывает конструкторы всех объектов-членов в порядке их объявления и, наконец, 3) выполнение тела функции конструктора. Это применяется рекурсивно, и для множественного наследования вы просто заменяете (1), вызывая все конструкторы базового класса в том порядке, в котором было объявлено наследование. Только виртуальное наследование добавляет настоящий дополнительный уровень сложности здесь.)
Ответ 2
Порядок вызова конструктора для вашей иерархии наследования будет следующим:
Base()
DerivedBaseTwo()
Base()
DerivedBaseOne()
Derived()
Порядок действительно четко определен и зависит от порядка, в котором вы указываете вывод для базовых классов и порядок, в котором вы объявляете членов класса для членов. (См. Ссылку из С++ Standard ниже.)
Вызывается ли конструктор Base() дважды?
Да
Конструктор класса Base()
вызывается здесь дважды, потому что из него выводятся два класса DerivedBaseTwo()
и DerivedBaseOne()
, поэтому конструктор базового класса вызывается один раз для каждого из них. Ваш класс Derived
имеет два различных субобъекта Base
через несколько путей (один через DerivedBaseOne()
, а другой - DerivedBaseTwo()
).
Иерархия классов, которые у вас есть с множественным наследованием, необычна, и это приводит к проблеме, называемой Виртуальный базовый класс.
Ссылка:
С++ 03 Стандарт: 12.6.2/5, Инициализация баз и членов
Инициализация должна выполняться в следующем порядке:
- Во-первых, и только для конструктора самого производного класса, как описано ниже, виртуальные базовые классы должны быть инициализированы в том порядке, в котором они появляются при первом переходе слева направо влево-вправо направленного ациклического графа базовых классов, где "слева направо" - это порядок появления имен базового класса в базе-спецификаторе производного класса.
- Затем прямые базовые классы должны быть инициализированы в порядке объявления, как они появляются в списке-спецификаторе-базовом (независимо от порядка инициализаторов mem).
- Затем нестатические члены данных должны быть инициализированы в том порядке, в котором они были объявлены в определении класса (опять же независимо от порядка mem-инициализаторов).
- Наконец, выполняется тело конструктора.
Ответ 3
На это отвечает: http://www.parashift.com/c++-faq-lite/multiple-inheritance.html#faq-25.14
Самыми первыми создаваемыми конструкторами являются виртуальные базовые классы в любом месте иерархии. Они выполняются в том порядке, в котором они отображаются в первом слева слева направо обход графика базовых классов, где слева направо относятся к порядку появления имен базового класса.
Поскольку ваше краткое объявление наследования перечисляет DerivedBaseTwo
, сначала его порядок построения будет выполнен до DerivedBaseOne
.
Итак, в вашем классе Derived
DerivedBaseTwo
и его цепочка создается сначала, а именно:
1 - Base
, затем DerivedBaseTwo
И затем DerivedBaseOne
и его цепочка:
2 - Base
, затем DerivedBaseOne
И затем:
3 - Derived
создается после всего остального.
Кроме того, при множественном наследовании следует помнить о проблемах с алмазным наследованием