【问题标题】:Are multiple-inherited constructors called multiple times?是否多次调用多个继承的构造函数?
【发布时间】:2011-11-16 09:03:07
【问题描述】:

多重继承的构造函数会被多次调用吗?构造函数的调用顺序是什么?这是否取决于继承列表中的顺序?

这是一个例子(只是为了说明情况,没有现实生活中的例子)。

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()?

【问题讨论】:

  • 您可能还在考虑虚拟继承,因为这与普通继承略有不同。
  • 继承构造函数是 c++11 的一个特性,意义完全不同。

标签: c++ inheritance constructor multiple-inheritance


【解决方案1】:

继承层次结构的构造函数调用顺序为:

Base()  
DerivedBaseTwo()  
Base()
DerivedBaseOne()  
Derived()

顺序确实定义明确,取决于您提到基类派生的顺序以及您在类中为成员声明成员的顺序。 (请参阅下面的 C++ 标准参考。)

Base() 构造函数会被调用两次吗?
是

Base() 类构造函数在此处被调用两次,因为 DerivedBaseTwo() 和 DerivedBaseOne() 两个类派生自它,因此基类构造函数被调用一次。您的 Derived 类有两个不同的 Base 子对象,通过多个路径(一个通过 DerivedBaseOne(),另一个通过 DerivedBaseTwo())。

具有多重继承的类的层次结构是不寻常的,它会导致称为 Diamond Shaped Inheritance Problem 的问题。为了避免这个问题,C++ 引入了 Virtual base class 的概念。


参考:

C++03 标准:12.6.2/5,初始化基和成员

初始化应按以下顺序进行:

——首先,并且仅对于如下所述的最派生类的构造函数,虚拟基类应按照它们在基类的有向无环图的深度优先从左到右遍历中出现的顺序进行初始化,其中“从左到右”是派生类 base-specifier-list 中基类名称的出现顺序。

——然后​​,直接基类应按照它们出现在 base-specifier-list 中的声明顺序进行初始化(不管 mem-initializers 的顺序如何)。

——然后​​,非静态数据成员应按照它们在类定义中声明的顺序进行初始化(同样不管 mem-initializers 的顺序)。

——最后,构造函数的主体被执行。

【讨论】:

  • 你确定吗?我认为重要的是声明的顺序(包括继承声明)。
  • 是的,@Kerrek 是对的。初始化列表的顺序对任何事情都没有影响(不幸的是。这会更直观 IMO)
  • 编译器会忽略初始化列表中成员的顺序(无关紧要)。不确定我是否同意 jalf 会更直观,但我确实认为初始化列表与声明的顺序不同应该是错误的。
  • 幸运的是,如果初始化列表不遵循声明的顺序,好的编译器会警告你。
  • @Kerrek:你说得对,指定继承的顺序和成员的成员声明
【解决方案2】:

按照您的编写方式,Derived 有 两个 类型为 Base 的不同子对象,并且每个子对象都从各自的 DerivedBaseXXX 构造函数调用它们自己的构造函数,它是其子对象。调用顺序按照声明顺序。

相比之下,你声明DerivedBaseXXX : virtual public Base,那么只有一个Base子对象,它的构造函数是从最派生的对象调用的,即Derived对象。

(更详细地解释一下:一个(可能是单继承)类的构造首先是 1)调用基类的构造函数,然后 2)按声明顺序调用所有成员对象的构造函数,最后3)执行构造函数体。这适用于递归,对于多重继承,您只需按照声明继承的顺序调用所有基类的构造函数来替换 (1)。只有虚拟继承在这里增加了真正的额外复杂性。)

【讨论】:

    【解决方案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 是在其他所有内容之后创建的。

    另外,对于多重继承,请注意Diamond Inheritance Problem

    【讨论】:

    • @Tux-D:可能不在他的示例代码中,但他将来可能会遇到它。猜猜我的措辞很糟糕~!
    猜你喜欢
    • 2013-09-06
    • 2011-07-06
    • 2016-06-19
    • 1970-01-01
    • 2015-02-11
    • 2013-10-18
    • 2015-01-17
    • 1970-01-01
    • 2015-06-20
    相关资源
    最近更新 更多