【问题标题】:Mixing virtual and non-virtual inheritance of a base class混合基类的虚拟和非虚拟继承
【发布时间】:2016-05-20 18:02:03
【问题描述】:

这是代码:

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 开始,通过构造HumanAnimal 和最后Centaur(我们从派生较少到派生最多)。

让我们从Human开始: Human继承自Biology,所以我们先调用Biology的构造函数。 现在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 实例。

这是什么逻辑?

请试着解释一下你的方式,我已经检查了很多关于这个的网站。但似乎没有一个能满足我的要求。

【问题讨论】:

  • 多么好的问题!

标签: c++ inheritance constructor multiple-inheritance virtual-inheritance


【解决方案1】:

从输出中可以清楚地看到两个Biology 对象被实例化。那是因为你只有一个继承virtual。两个基类实例是在可怕的钻石问题中产生歧义的原因,解决方案是(如我们所知)bothBiologyvirtual 的继承。

层次结构回顾:

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

好的,让我们记住这些规则再次阅读输出:

  • 基类在派生类之前构造。
  • 基类是按照它们在base-specifier-list中出现的顺序构造的。
  • 虚拟基类由最派生类在非虚拟基类之前构造 - see this

第一个输出 - Animal virtually 继承自 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

第二个输出 - Human virtually 继承自 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.

——然后​​,直接基类在 出现在 base-specifier-list 中的声明顺序 (不管 mem-initializers 的顺序如何)。

...

【讨论】:

  • 这个答案是黄金。谢谢;)
【解决方案2】:
  1. 虚拟继承自Biology 的所有基类在它们之间共享一个Biology 基类实例。
  2. Biology 非虚拟继承的所有基类都有一个Biology 实例。

每个类别都有一个基地,因此您有一个 Biology 实例由 Human 引入(原则上与其他人共享)和一个实例由 Animal 引入(从未与任何其他基地共享类)。

【讨论】:

  • 感谢您的回答,尽管一年后哈哈
【解决方案3】:

非虚拟继承是一种排他关系,就像成员资格一样。一个类可以是给定完整对象中另一个类的非虚拟基类。

这意味着一个类可以覆盖非虚基类的虚函数而不会引起冲突或问题。

构造函数也可以可靠地初始化非虚基。

只有虚基可以是一个完整对象的许多间接基的直接基类。因为可以共享虚拟基类,所以覆盖器可能会发生冲突。

构造函数可以尝试初始化 ctor-init-list 中的虚拟基础子对象,但如果进一步派生该类,则 ctor-init-list 的该部分将被忽略。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-30
    • 1970-01-01
    • 2012-09-27
    • 1970-01-01
    相关资源
    最近更新 更多