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