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

Смешение виртуального и не виртуального наследования базового класса

Это код:

struct Biology
{    
    Biology() { cout << "Biology CTOR" << endl; }
};

struct Human : Biology
{    
    Human() { cout << "Human CTOR" << endl; }
};

struct Animal : virtual Biology
{
    Animal() { cout << "Animal CTOR" << endl; }
};

struct Centaur : Human, Animal
{
    Centaur() { cout << "Centaur CTOR" << endl; }
};

int main()
{   
   Centaur c;

   return 0;
}

Этот код печатает:

Biology CTOR
Biology CTOR
Human CTOR
Animal CTOR
Centaur CTOR

Почему?

Так как мы создаем объект Centaur, мы начинаем с построения Centaur, создавая Human, Animal и, наконец, Centaur (мы начинаем с менее производного до самого полученного).

Пусть начнется с Human: Human наследуется от Biology, поэтому сначала вызываем конструктор Biology. Теперь, когда строится базовый класс Human, мы можем, наконец, построить сам Human. Но вместо этого Biology снова создается!

Почему? Что происходит за кулисами?

Обратите внимание, что это было полностью умышленное удержание Animal, наследующее фактически от Biology, и в то же время оно также преднамеренно оставляло Human, не наследуя от Biology.

Мы решим Dreaded Diamond неверным образом: как Человек, так и Животное должны фактически наследовать Биологию, чтобы сделать эту работу.

Мне просто интересно.

Также см. этот код:

struct Biology
{    
    Biology() { cout << "Biology CTOR" << endl; }
};

struct Human : virtual Biology
{
    Human() { cout << "Human CTOR" << endl; }
};

struct Animal : Biology
{    
    Animal() { cout << "Animal CTOR" << endl; }
};

struct Centaur : Human, Animal
{
    Centaur() { cout << "Centaur CTOR" << endl; }
};

int main()
{   
   Centaur c;

   return 0;
}

Здесь мы имеем Human, наследуем фактически от Biology, а Animal устанавливается на наследование "классическим способом".

Но на этот раз выход отличается:

Biology CTOR
Human CTOR
Biology CTOR
Animal CTOR
Centaur CTOR

Это потому, что Centaur сначала наследует от Human и , а затем от Animal.

Если бы порядок был обратным, мы бы достигли того же результата, что и раньше, в первом примере - два экземпляра Biology, которые строятся в строке.

Какова логика этого?

Пожалуйста, попробуйте объяснить свой путь, я уже проверил множество сайтов, говорящих об этом. Но никто не удовлетворяет мою просьбу.

4b9b3361

Ответ 1

Из вывода видно, что два объекта Biology создаются. Это потому, что вы сделали только одно наследование virtual. Два экземпляра базового класса являются причиной двусмысленности в проблеме ужасных алмазов, и решение состоит в том, чтобы сделать (как мы знаем) оба наследования Biology virtual.

Повторение иерархии:

Biology  Biology
   |       |     # one and only one inheritance virtual
Human     Animal
    \     /
    Centaur

Хорошо, прочитайте вывод снова с учетом этих правил:

  • Базовые классы создаются перед производными классами.
  • Базовые классы строятся в том порядке, в котором они отображаются в списке-спецификаторе базы.
  • Виртуальные базовые классы строятся до не виртуальных по наиболее производному классу - см. это.

1-й выход - Animal virtual ly наследует от Biology:

Biology CTOR     # virtual base class inherited from Animal
Biology CTOR     # non-virtual base class of Human
Human CTOR       # Human itself
Animal CTOR      # Animal virtual base class already constructed
Centaur CTOR

2-й вывод - Human virtual ly наследует от Biology:

Biology CTOR     # virtual base class inherited from Human
Human CTOR       # Human virtual base class already constructed
Biology CTOR     # non-virtual base class of Animal
Animal CTOR      # Animal itself
Centaur CTOR

Более информативный стандартный параграф ([class.base.init]/10) :

В конструкторе без делегирования инициализация продолжается в в следующем порядке:

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

- Затем прямые базовые классы инициализируются в порядок декларации, поскольку они отображаются в списке-спецификаторе базы (независимо от порядка mem-инициализаторов).

...

Ответ 2

Не виртуальное наследование - это эксклюзивные отношения, такие как членство. Класс может быть не виртуальным базовым классом одного другого класса в заданном полном объекте.

Это означает, что класс может переопределять виртуальные функции не виртуального базового класса, не вызывая конфликтов или проблем.

Конструктор также может надежно инициализировать не виртуальные базы.

Только виртуальные базы могут быть прямыми базовыми классами многих косвенных оснований полного объекта. Поскольку виртуальный базовый класс может использоваться совместно, переопределители могут конфликтовать.

Конструктор может попытаться инициализировать виртуальный базовый подобъект в ctor-init-list, но если класс будет получен, эта часть списка ctor-init будет проигнорирована.

Ответ 3

  • Все базовые классы, которые наследуют фактически от Biology, разделяют один экземпляр базы Biology между ними.
  • Все базовые классы, которые наследуют не виртуально от Biology, имеют один экземпляр каждого из Biology.

У вас есть одна база в каждой категории, поэтому у вас есть один экземпляр Biology, внесенный Human (и в принципе разделяемый с другими), и один экземпляр, внесенный Animal (никогда не разделяемый с какой-либо другой базой класс).