2012-11-16 30 views
5

我有一個鑽石的問題看起來是這樣的:複雜的菱形問題:C++虛繼承

__ A 
/ |\ 
| B | \ 
v|/v v\|v \v 
B2 B3 C 
    \v /v /
    B4 /
    \ /
     D 

我試過很多方法,使最佳的虛擬繼承得到沒有重複,但我無法找到一個解決方案。 A類包含一個位置。下面是一個輸出示例:

Call: A() position pointer is: 0x2203be8 
Call: B() 
Call: B2() position pointer is: 0x2203be8 
Call: B3() position pointer is: 0x2203be8 
Call: C() position pointer is: 0x2203a28 
Call: B4() position pointer is: 0x2203be8 
Call: D() position pointer is: 0x2203a28 

爲什麼D和C沒有相同的位置指針?爲什麼這個A :: position沒有構造函數?我應該做些什麼虛擬繼承來解決這個問題?謝謝。

編輯:

下面是一個代碼示例:

class A; 
class B; 
class B2 : public virtual B, public virtual A; 
class B3 : public virtual B, public virtual A; 
class C : public virtual A; 
class B4 : public virtual B2, public virtual B3; 
class D : public B4, public C; 

編輯2: 爲了使輸出,我把這個代碼的每個構造內:

A::A() 
{ 
    std::cerr << "Call: A() position pointer is: " << &_position << std::endl; 
} 
+4

這是一個可怕的鑽石。 –

+0

鑽石內的鑽石! –

+0

您需要提供您正在使用的實際繼承關係。考慮在單個答案中使用格式(只提供'derived_type:[virtual] base,[virtual] base2 ...')。解釋你如何生成輸出也很有趣 –

回答

1

你什麼碼目前有?它看起來像解決方案將是:

class D; 
class C : public virtual D; 
class B4 : public virtual D; 
class B2 : public virtual B4; 
class B3 : public virtual B4; 
class B : public B2, public B3; 
class A : public B2, public B3, public C; 

根據您的圖。如果我讀錯了,A爲基礎,而不是D.然後就需要像這樣:

class A; 
class B; 
class B2 : public virtual B, public virtual A; 
class B3 : public virtual B, public virtual A; 
class C : public virtual A; 
class B4 : public virtual B2, public virtual B3; 
class D : public B4, public C; 
+0

哦!那麼讓我改寫爲A然後。 – OmnipotentEntity

+1

爲了使它更加明顯,他們從第二組中缺少的'虛擬'就是'D'上的那些'虛擬'。 –

+0

第二個是好代碼 –

0

爲什麼d和C不具備位置相同的指針?

因爲你是非虛擬地從B4和C繼承D.這意味着你有兩個A(和兩個指針)的副本。

在d構造& B4 ::位置從&Ç::位置

爲什麼有這個A ::位置沒有構造有什麼不同?

不知道,你的A類有多個構造函數,並且C :: C()調用默認無聲構造函數的機會?

我應該做些什麼虛擬繼承來解決這個問題?

使所有虛擬。這意味着您需要顯式調用D :: D()中的每個構造方法(即A :: A(),B :: B(),B2 :: B2(),B3 :: B3(),C :: C() )。

Tbh我相信你應該重新考慮你的層次結構。我不知道細節,但似乎你的問題通過組件設計有一個更清潔的解決方案。

+0

不,我只有一個A的構造函數,它似乎沒有初始化位置的第二個指針 –

+1

即使當'B4'和'C'從非虛擬繼承時,繼承對象將會共享一個'A'爲他們兩個,因爲他們都對'A'使用虛擬繼承。 D''如何繼承並不重要。也沒有必要顯式調用'D'的每個構造函數。 – bames53

4

既然你說下面的代碼,它適用於我的實現,是打破了你然後顯然代碼不是問題。問題在於你的設置有其他問題。也許是一個編譯錯誤。你應該縮小什麼可能是問題的原因;因爲代碼本身被排除爲一個問題,所以最好的下一步就是更新你的編譯器。

在任何情況下,這個問題都會使您的設置更加具體。如果你確實找到了可能適用於其他人的解決方案,那麼你應該回來併發布它。在此之前,我正在投票結束這個問題。


我試圖重現您的問題。下面是我使用的代碼:

#include <iostream> 

struct A { int a; }; 
struct B { int b; }; 
struct B2 : virtual B, virtual A {}; 
struct B3 : virtual B, virtual A {}; 
struct B4 : virtual B2, virtual B3 {}; // these virtuals are unnecessary in this case... 
struct C : virtual A {}; 
struct D : B4, C {}; 

int main() { 
    D d; 
    std::cout << &((B4*)&d)->a << '\n'; 
    std::cout << &((B3*)(B4*)&d)->a << '\n'; 
    std::cout << &((B2*)(B4*)&d)->a << '\n'; 
    std::cout << &((A*)(B2*)(B4*)&d)->a << '\n'; 
    std::cout << &((A*)(B3*)(B4*)&d)->a << '\n'; 
    std::cout << &((C*)&d)->a << '\n'; 
    std::cout << &((A*)(C*)&d)->a << '\n'; 
} 

但如預期,我得到的結果,其中a成員是每個對象相同。我得到同樣的結果,如果我用打印地址列於構造以及:http://ideone.com/8FdQ1O

如果我做一個微小的變化,由C的定義中移除virtual關鍵字:

... 
struct C : A {}; 
... 

version using constructors

然後,我確實看到了你描述的問題,其中C具有它自己的一個不同於B2,B3和B4所使用的虛擬子對象的子對象。

您確定您在所需的所有地方都使用virtual關鍵字嗎?你顯示的結果似乎表明你錯過了某個地方。另外我注意到,你顯示的輸出結果並不反映你所顯示的代碼片段的相同構造函數的順序;輸出首先顯示A(),但代碼指示B()應該先執行。


虛擬繼承的工作方式是,大多數派生類型將包含一個單一的虛擬子對象爲虛擬地繼承樹中的任何地方繼承了每種類型。另外最派生類型將包含子對象的非虛擬繼承的每個實例:

struct A {}; 
struct B : virtual A {}; 
struct C : A, B {}; 
struct D : virtual A, C {}; 
struct E : A, D {}; 
struct F : virtual A, E {}; 
struct G : A, F {}; 

G g; 

g含有總共四個A子對象;每次一個A是非虛擬遺傳的(在C,EG中),並且對於所有時間A都被虛擬地繼承(在B,DF中)。

+0

不幸的是,我添加了所有需要的虛擬關鍵字,我仍然得到了錯誤。我使用了4.6.3 –

+0

@GuillaumeRacicot你是說當你運行程序時,我提供了你看到的問題?或者你是否以某種方式修改了你的代碼?您必須提供足夠的實際代碼才能讓任何人診斷問題,因爲已提供的碎片似乎不包含問題的原因。 – bames53

+0

你發給我的程序是完全一樣的。問題是我的代碼與工作代碼相同...... –