2012-04-24 72 views
1

我用虚拟继承编写了这个程序,我有几个问题。虚拟继承在这种情况下的行为

#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

什么编译/版本? – 2012-04-24 19:08:42

+0

拥抱?你想要一个'B2'子对象被构造两次??? – curiousguy 2012-07-29 04:53:20

回答

3

从标准的确切的报价是12.6.2p10:

在非委托构造函数,以下列顺序进行初始化:

- 第一,并且仅用于最构造派生类(1.8),虚拟基类以它们出现在基类的有向无环图的深度优先从左到右遍历的顺序进行初始化,其中“从左到右”是派生类base-specifier-list中的基类的外观。

- 然后,直接基类以声明顺序初始化,因为它们出现在base-specifier-list中(不管mem-initializers的顺序如何)。

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

- 最后,执行构造函数体的复合语句。

我认为关键是深度优先的粗体文字从左到右V1类别是X的虚拟基础,即左侧V2,即使它是更深的在层次结构中。

你的情况层次图是这样的:

  X 
     / \\ 
     D1  D2 
     || /\\ 
     V1 B3 V2 
     |  /\ 
     B1 B1* B2 

凡单行标识平原继承和双线是虚拟继承。请注意,在完整对象X中有两个B1的实例。现在,如果你执行深度优先左到右搜索,你会走的节点按以下顺序:

[ B1, V1, D1, B3, B1*, B2, V2, D2, X ] 

和虚拟基础是V1V2D2,这是该命令他们将被构建。 V1要求建造B1V2需要B1*B2建设,D2需要B3,所以顺序应该是:

[ B1, V1, B1*, B2, V2, B3, D2, D1, X ] 

B1建设由V1B1*B2触发必须订购V2之前,B3被触发作为一个依赖D2。在这一点上,所有的虚拟基地都建成了,非虚拟基地开始建设。 X由于虚拟碱基的依赖关系而未被初始化的唯一非虚拟碱基是D1

如果钻石被关闭(说V1V2继承几乎从B1,那么将是B1只有一个实例,它是将建造的第一子对象。

+0

虚拟基类按照它们出现在二进制的深度优先从左到右的遍历的顺序进行初始化基础类的非循环非循环图....看起来有点棘手,我明白:( – Invictus 2012-04-24 19:22:38

+0

如果你拿出你可能画出的图的镜像,现在说'V2'在'V1'的左边, B2'和'B3'也出现在图的左侧,因为它的菱形结构 – Invictus 2012-04-24 19:28:31

+2

标准说*其中“从左到右”是派生类中基类的出现顺序base-speficier-list * 。在这种情况下,“从左到右”是明确定义的。 – 2012-04-24 19:44:47

0

C++总是首先构造“第一个”或“最基础”类。然后按顺序遍历继承树并构造每个连续的派生类。

+0

这有一个菱形结构,你能告诉我哪个路径被赋予一个偏好和什么基础? – Invictus 2012-04-24 19:13:51

+1

@Ritesh:要清楚,没有*菱形*在这个层次结构中有多个'B1'基础 – 2012-04-24 19:22:40