23

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

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 «s первым. Теперь, когда построен базовый класс Human, мы можем, наконец, построить сам Human. Но вместо этого Biology снова строится!

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

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

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

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

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

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, построенные в строке.

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

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

+2

Какой прекрасный вопрос! – Hexaholic

ответ

36

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

Recap иерархии:

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

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

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

первый выход - Animalvirtual 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's virtual base class already constructed 
Centaur CTOR 

второй выход - Humanvirtual LY наследует от Biology:

Biology CTOR  # virtual base class inherited from Human 
Human CTOR  # Human's 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) , виртуальные базовые классы инициализируются в порядке , они появляются на первом пересечении влево-вправо направленного ациклического графа базовых классов, где «слева направо» является порядком внешний вид базы классы в производном классе base-specifier-list.

- Тогда прямые базовые классы инициализируются в порядке декларации, как они появляются в базовой спецификатора-лист (независимо от порядка MEM-инициализаторах).

...

+1

Этот ответ золотой. Спасибо;) – gedamial

2

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

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

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

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

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

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

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

+0

Спасибо за ответ, хотя через год хаха – gedamial

Смежные вопросы