在以下代码中:
class A
{
public:
int x;
A(int x):x(x){}
};
class B: public virtual A
{
public:
B(int x):A(x){}
};
class C: public virtual A
{
public:
C(int x):A(x){}
};
class D: public B, public C
{
public:
D(int x):B(x++), C(x++), A(x++){}
};
两个问题:
A(...)
?D(int x):B(x++), C(x++), A(x++){}
和D(int x):A(x++), B(x++), C(x++){}
都会为cout<<D(10).x
提供相同的结果,为什么?答案 0 :(得分:6)
为什么我需要在D的初始化列表中添加A(...)?
这是因为必须在所有其他子对象之前初始化虚拟基础子对象。由于A
没有默认构造函数,因此需要在A
中明确初始化虚拟D
子对象,并指定要用它构造哪个参数。
当执行B
和C
个基础子对象的构造函数时,它们将没有要初始化的基础A
子对象(已经在之前完成)。因此,它们传递给A
的构造函数的参数是无关紧要的。
D(int x):B(x++), C(x++), A(x++){}
和D(int x):A(x++), B(x++), C(x++){}
都会为cout<<D(10).x
提供相同的结果,为什么?
如上所述,这是因为无论如何都首先初始化虚拟基础子对象。
通常,类的子对象的初始化顺序决不会取决于它们在构造函数的初始化列表中的显示顺序。根据C ++ 11标准的第12.6.2 / 10段:
在非委托构造函数中,初始化按以下顺序进行:
- 第一个,仅针对派生程度最高的类(1.8)的构造函数,虚拟基类初始化为 它们出现在基类的有向无环图的深度优先从左到右遍历中的顺序, 其中“从左到右”是派生类base-specifier-list中基类出现的顺序。
- 然后,直接基类按声明顺序初始化,因为它们出现在base-specifier-list 中 (无论 mem-initializers 的顺序如何)。
- 然后,按照在类定义中声明的顺序初始化非静态数据成员 (无论 mem-initializers 的顺序如何)。
- 最后,执行构造函数体的复合语句。
答案 1 :(得分:1)
虚拟基类仅由派生程度最高的类初始化。也就是说,如果在您的示例中创建D
的实例,A
将仅通过其在D
的mem-initialiser列表中的出现进行初始化。它在B
和C
的mem-initialiser列表中的出现完全被忽略。
这也是您必须在A
中初始化D
的原因:A
没有默认的ctor,因此D
必须知道如何初始化它。< / p>