2016-01-10 144 views
2

說我們有經典的多重繼承模式:爲什麼不用只有一個虛擬繼承的鑽石繼承?

class Base { 
    int baseMember; 
}; 

class A : public Base { 
    int aMember; 
}; 

class B : public Base { 
    int member; 
}; 

class Derived : public A, public B { 
    int derivedMember; 
}; 

這將奠定了在內存中派生的對象是這樣的:

  • 基地的領域< - (基地*)(A *)這個| (A *)this |
  • A的字段
  • Base的字段< - (Base *)(B *)this | (B *)這
  • B的字段
  • Derived的字段

爲了防止基礎的字段被複制,則缺省的方法是inherite幾乎從鹼和B:

class A : public virtual Base { 
... 
class B : public virtual Base { 

這會導致以下佈局:

  • 抵消基地的字段< - (A *)this |這
  • A的領域
  • 偏移到基地的領域< - (B *)這個
  • B的領域
  • Derived的領域
  • 基地的領域< - (基地*)這個| (Base *)(A *)this | (Base *)(B *)

解決了這個問題,您必須權衡應用偏移量從其他類訪問Base字段(和虛函數)的問題。

但爲什麼以下不可能?

  • Base's fields < - (Base *)(B *)this | (Base *)(A *)this | (A *)this |這
  • A的領域
  • 偏移到基地的領域< - (B *)這個
  • B的領域
  • Derived的領域

這將使A和衍生無開銷訪問基地,沒有重複,甚至將大小縮小1個整數。但是,如果我嘗試它,編譯器仍然複製Base的字段(將它們放在B的字段後面)。

note:我知道有類似的問題已經被問到,但我還沒有看到爲什麼這是不可能的。

+1

在您看到Derived之前,您需要佈置A和B.一旦你選擇了佈局,它就是一成不變的。派生和所有其他類必須使用它的A和B子對象。 –

回答

2

您需要在看到派生之前佈置A和B.一旦你選擇了佈局,它就是一成不變的。派生和所有其他類必須使用它的A和B子對象。 (虛擬基礎位置可能因爲每個完整的類而有所不同,並且不被視爲佈局的一部分)。

現在,如何在虛擬繼承的情況下佈置A?它必須包含offset-base字段,因爲我們不知道程序的其他部分將如何使用A.它可能是一些尚未編寫的派生類的第一個基類,或第七個。但是使用A的函數必須有一種方法來將A轉換爲Base,而不必等待這些派生類被定義。

同樣的當然是關於B.

真那麼A和B兩個孔坦自己offsset - 底部的領域。因爲它可能不是繼承A或B的唯一類,所以派生是無法做出決定的。

+0

A不需要偏移量字段。對於常規繼承,您可以設置基本字段始終在A字段之前的規則。你不能爲B做這件事,也不會在Derived中造成麻煩。 –

+0

「A不需要偏移字段」。錯誤。 「你可以設置規則基地的領域總是在A的領域之前」不,你不能。你爲什麼認爲A可能而不是B?編譯器沒有理由相對於另一個偏好。 –

+0

當我說「A不需要偏移字段」時,我的意思是在我的問題底部的假設內存佈局中。我知道你不能讓C++來做到這一點。我希望A繼承非虛擬化(所以它沒有偏移字段),B繼承虛擬,並且在派生B的偏移量指向A的基本部分。 –