简体   繁体   English

混合基类的虚拟和非虚拟继承

[英]Mixing virtual and non-virtual inheritance of a base class

This is the code:这是代码:

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;
}

This code prints:此代码打印:

Biology CTOR
Biology CTOR
Human CTOR
Animal CTOR
Centaur CTOR

Why?为什么?

Since we create a Centaur object, we start from building the Centaur by constructing Human , Animal and finally Centaur (we start from the less derived to the most derived).因为我们创建了一个Centaur对象,所以我们从构建Centaur开始,构建HumanAnimal ,最后构建Centaur (我们从较少派生到派生最多)。

Let's start from Human : Human inherits from Biology , so we call Biology 's constructor first.让我们从Human开始: Human继承了Biology ,所以我们先调用Biology的构造函数。 Now that Human 's base class is constructed, we can finally construct the Human itself.既然已经构建了Human的基类,我们终于可以构建Human本身了。 But instead, Biology gets constructed again!但相反, Biology再次被构建!

Why?为什么? What's happening behind the scenes?幕后发生了什么?

Please note that it was completely intentional leaving Animal inheriting virtually from Biology and, at the same time, it was also intentional leaving Human non-virtually inheriting from Biology .请注意,这完全是故意让Animal虚拟继承Biology ,同时也是故意让Human非虚拟继承Biology

We are solving the Dreaded Diamond in an incorrect way: both Human and Animal should virtually inherit Biology to make this work.我们正在以错误的方式解决可怕的钻石人类和动物都应该实际上继承生物学来完成这项工作。

I'm just curious.我只是好奇。

Also, see this code:另外,请参阅此代码:

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;
}

Here we have Human inheriting virtually from Biology , while Animal is set to inherit in the "classic way".在这里,我们将Human虚拟地从Biology继承,而Animal设置为以“经典方式”继承。

But this time, the output is different:但这一次,输出不同:

Biology CTOR
Human CTOR
Biology CTOR
Animal CTOR
Centaur CTOR

This because Centaur inherits at first from Human and then from Animal .这是因为Centaur首先Human继承然后Animal继承。

Had the order been the inverse, we'd have achieved the same result as before, in the first example - two Biology instances being constructed in a row.如果顺序是相反的,我们将获得与之前相同的结果,在第一个示例中 - 连续构造两个Biology实例。

What's the logic of this?这是什么逻辑?

Please try to explain your way, I've already checked tons of websites speaking about this.请尝试解释你的方式,我已经检查了大量关于这个的网站。 But none seems to satisfy my request.但似乎没有一个能满足我的要求。

It's clear from the output that two Biology objects are instantiated .从输出中可以清楚地看出,两个Biology对象已被实例化 That is because you've made only one inheritance virtual .那是因为你只做了一个继承virtual Two base class instances is the cause of ambiguity in dreaded diamond problem and the solution is to make (as we know) both inheritances of Biology virtual .两个基类实例是可怕的菱形问题中歧义的原因,解决方案是(如我们所知)使Biology两个继承成为virtual

Recap of the hierarchy:层次结构回顾:

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

Ok, let's read the output again with these rules in mind:好的,让我们记住这些规则再次阅读输出:

  • Base classes are constructed before derived classes.基类在派生类之前构造。
  • Base classes are constructed in order in which they appear in the base-specifier-list .基类是按照它们在base-specifier-list 中出现的顺序构造的。
  • Virtual base classes are constructed before non-virtual ones by the most derived class - see this .虚拟基类由最派生的类在非虚拟基类之前构造- 请参阅this

1st output - Animal virtual ly inherits from Biology : 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's virtual base class already constructed
Centaur CTOR

2nd output - Human virtual ly inherits from Biology :第二个输出 - Human virtual继承自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

More informative standard paragraph ( [class.base.init]/10 ) :更多信息标准段落[class.base.init]/10

In a non-delegating constructor, initialization proceeds in the following order:在非委托构造函数中,初始化按以下顺序进行:

— First, and only for the constructor of the most derived class (1.8), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list . — 首先,并且仅对于最派生类 (1.8) 的构造函数,虚拟基类按照它们出现在基类的有向无环图的深度优先从左到右遍历中出现的顺序进行初始化,其中“左-to-right” 是基类在派生类base-specifier-list 中的出现顺序。

— Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers ). — 然后,直接基类按照它们出现在base-specifier-list中的声明顺序进行初始化(无论mem-initializers的顺序如何)。

... ...

Non virtual inheritance is an exclusive relationship, like membership.非虚拟继承是一种排他关系,就像成员资格一样。 A class can be the non-virtual base class of one other class in a given complete object.一个类可以是给定完整对象中另一个类的非虚拟基类。

This implies that a class can override virtual functions of a non virtual base class without causing conflicts or issues.这意味着一个类可以覆盖非虚基类的虚函数而不会引起冲突或问题。

A constructor can also initialize non virtual bases reliably.构造函数也可以可靠地初始化非虚拟基类。

Only virtual bases can be direct base classes of many indirect bases of a complete object.只有虚拟基类可以是一个完整对象的许多间接基类的直接基类。 Because a virtual base class can be shared, overriders can conflict.由于可以共享虚拟基类,因此覆盖程序可能会发生冲突。

A constructor can try to initialize a virtual base subobject in the ctor-init-list, but if the class is further derived, that part of the ctor-init-list will be ignored.构造函数可以尝试初始化 ctor-init-list 中的虚拟基子对象,但如果进一步派生该类,则 ctor-init-list 的那部分将被忽略。

  1. All the base classes that inherit virtually from Biology share one instance of Biology base between them.Biology虚拟继承的所有基类在它们之间共享一个Biology基实例。
  2. All the base classes that inherit non-virtually from Biology have one instance each of Biology .所有的基类,从继承非虚拟Biology有一个实例每个Biology

You have one base in each category, therefore you have one instance of Biology brought in by Human (and in principle shared with others) and one instance brought in by Animal (never shared with any other base class).您在每个类别中都有一个基础,因此您有一个由Human引入的Biology实例(原则上与其他人共享)和一个由Animal引入的实例(从未与任何其他基类共享)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM