多重继承的构造函数是多次调用的吗?并且构造函数的调用顺序是什么?这取决于继承列表中的顺序吗?
这是一个例子(它只是为了使情况清晰,没有现实生活中的例子)。
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()
?
你编写它的方式,Derived
有两个不同的Base
类型的子对象,每个子对象都从它们是子对象的相应DerivedBaseXXX
构造函数中调用它们自己的构造函数。调用顺序遵循声明顺序。
相比之下,你声明DerivedBaseXXX : virtual public Base
,那么只有一个Base
子对象,并且它的构造函数是从最派生的对象调用的,即来自Derived
对象。
(更详细地解释一下:A(可能是单继承)类首先构造1)调用基类的构造函数,然后2)按照它们的声明顺序调用所有成员对象的构造函数,最后3)执行构造函数体。这适用于递归,对于多继承,您只需通过按声明继承的顺序调用所有基类的构造函数来替换(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的顺序如何)。
- 最后,执行构造函数的主体。
这可以在: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