Подтвердить что ты не робот

С++, являются многоуровневыми конструкторами, называемыми несколько раз?

Многочисленные наследуемые конструкторы называются несколько раз? И в каком порядке называются конструкторы? Это зависит от порядка в списке наследования?

Вот пример (это только для того, чтобы сделать ситуацию ясной, не реальный пример).

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() сначала?

Спасибо.

4b9b3361

Ответ 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 создается после всего остального.

Кроме того, при множественном наследовании следует помнить о проблемах с алмазным наследованием