2012-04-24 69 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