我用虚拟继承编写了这个程序,我有几个问题。
#include<iostream>
using namespace std;
class B1
{
public:
B1()
{
cout << "In B1 constructor\n";
}
};
class V1 : public B1
{
public:
V1()
{
cout << "In V1 constructor\n";
}
};
class D1 : virtual public V1
{
public:
D1()
{
cout << "In D1 constructor\n";
}
};
class B2
{
public:
B2()
{
cout << "In B2 constructor\n";
}
};
class B3 {
public:
B3()
{
cout << "In B3 constructor\n";
}
};
class V2 : public B1, public B2
{
public:
V2()
{
cout << "In V2 constructor\n";
}
};
class D2 : public B3, virtual public V2
{
public:
D2()
{
cout << "In D2 constructor\n";
}
};
class X : public D1, virtual public D2
{
public:
X()
{
cout << "In X constructor\n";
}
};
int main()
{
X x;
return 0;
}
计划的输出:
In B1 constructor
In V1 constructor
In B1 constructor
In B2 constructor
In V2 constructor
In B3 constructor
In D2 constructor
In D1 constructor
In X constructor
我期待这样的输出:
In B1 constructor
In B2 constructor
In V2 constructor
In B2 constructor
In D2 constructor
In B1 constructor
In V1 constructor
In D1 constructor
In X constructor
首先构造虚拟基类的对象,然后构造另一个基类对象。有人可以解释这种行为吗?
答案 0 :(得分:3)
标准的确切引用是12.6.2p10:
在非委托构造函数中,初始化按以下顺序进行:
- 首先,仅对于派生程度最高的类(1.8)的构造函数,虚拟基类按照它们在有向无环图的深度优先从左到右遍历中出现的顺序进行初始化。基类,其中“从左到右”是派生类base-specifier-list中基类出现的顺序。
- 然后,直接基类按声明顺序初始化,因为它们出现在base-specifier-list中(无论mem-initializers的顺序如何)。
- 然后,非静态数据成员按照它们在类定义中声明的顺序进行初始化(同样不管mem-initializers的顺序如何)。
- 最后,执行构造函数体的复合语句。
我认为关键是粗体文本中的深度优先从左到右。 V1
类是X
的虚拟基础,即{em> <{1}},即使更深层次结构。
您的案例中的层次结构图如下:
V2
其中单行标识普通继承,双行是虚拟继承。请注意,完整对象 X
/ \\
D1 D2
|| / \\
V1 B3 V2
| / \
B1 B1* B2
中有两个B1
个实例。现在,如果您执行深度优先从左到右搜索,您将按以下顺序遍历节点:
X
虚拟基础是[ B1, V1, D1, B3, B1*, B2, V2, D2, X ]
,V1
,V2
,这是它们的构造顺序。 D2
需要构建V1
。 B1
需要构建V2
和B1*
,B2
需要D2
,因此订单应为:
B3
由[ B1, V1, B1*, B2, V2, B3, D2, D1, X ]
触发B1
构造,V1
和B1*
必须在B2
之前订购,V2
作为依赖项触发B3
。此时,构建所有虚拟基础并开始构建非虚拟基础。由于虚拟基础的依赖性而未初始化的唯一非虚拟基础D2
为X
。
如果钻石已关闭(假设D1
和V1
几乎从V2
继承,则只有B1
的一个实例,它将是第一个要构建的子对象。
答案 1 :(得分:0)
C ++总是首先构造“第一”或“最基础”类。然后按顺序遍历继承树并构造每个连续的派生类。