2010-07-04 119 views
3

我在閱讀關於虛擬繼承的Wikipedia文章。我跟着整篇文章,但我不能真正遵循最後一段C++中的虛擬繼承

這是通過提供 哺乳動物和WingedAnimal有虛表 指針自實施(或「vpointer」),例如, 內存之間的偏移 哺乳動物的開始和它的 動物部分直到運行時才知道。因此蝙蝠變成 (vpointer,Mammal,vpointer,WingedAnimal,Bat,Animal)。 有兩個vtable指針,每個 繼承層次結構,幾乎 繼承動物。在這個例子中,一個用於Mammal的 ,另一個用於WingedAnimal。 因此物體尺寸 增加了兩個指針,但是現在的 只有一個動物而沒有 含糊不清。 Bat 類型的所有對象將具有相同的vpointers,但每個蝙蝠對象都將包含其自己的唯一 動物對象。如果另一個類從哺乳動物 繼承,如 松鼠,然後在松鼠在 哺乳動物對象的vpointer將 從在一個BAT中 哺乳動物對象的vpointer不同,儘管它們 仍然可以基本上在同一 特殊情況表示松鼠 部分物體具有與蝙蝠部分相同的尺寸 ,因爲那麼從哺乳動物到動物 部分的距離是相同的。這個vtable不是 真的是一樣的,但是它們中的所有信息(距離)都是本質的 。

有人可以擺脫這一點更多的光。

回答

6

有時候,你真的需要看一些代碼/圖表:)請注意,標準中沒有提到這個實現細節。

首先,讓我們看看如何實現用C方法++:

struct Base 
{ 
    void foo(); 
}; 

這是類似的:

struct Base {}; 

void Base_foo(Base& b); 

而事實上,當你看到一個方法調用一個調試器中,您經常會將this參數看作第一個參數。它有時被稱爲隱式參數。

現在,上虛擬表。在C和C++中,可以使指針起作用。 V表基本上是函數指針表:

struct Base 
{ 
    int a; 
}; 

void Base_set(Base& b, int i) { b.a = i; } 
int Base_get(Base const& b) { return b.a; } 

struct BaseVTable 
{ 
    typedef void (*setter_t)(Base&, int); 
    typedef int (*getter_t)(Base const&); 

    setter_t mSetter; 
    getter_t mGetter; 

    BaseVTable(setter_t s, getter_t g): mSetter(s), mGetter(g) {} 
} gBaseVTable(&Base_set, &Base_get); 

現在我可以這樣做:現在

void func() 
{ 
    Base b; 
    (*gBaseVTable.mSetter)(b, 3); 
    std::cout << (*gBaseVTable.mGetter)(b) << std::endl; // print 3 
} 

,就到了繼承。讓我們創建另一個結構

struct Derived: Base {}; // yeah, Base does not have a virtual destructor... shh 

void Derived_set(Derived& d, int i) { d.a = i+1; } 

struct DerivedBaseVTable 
{ 
    typedef void (*setter_t)(Derived&,int); 
    typedef BaseVTable::getter_t getter_t; 

    setter_t mSetter; 
    getter_t mGetter; 

    DerivedBaseVTable(setter_t s, getter_t g): mSetter(s), mGetter(g) {} 
} gDerivedBaseVTable(&Derived_set, &Base_get); 

而且使用:

void func() 
{ 
    Derived d; 
    (*gDerivedBaseVTable.mSetter)(d, 3); 
    std::cout << (*gDerivedBaseVTable.mGetter)(d) << std::endl; // print 4 
} 

但如何實現自動化嗎?

  • 你只需要具有至少一個虛函數
  • 類的每個實例都將包含一個指向虛函數表作爲它的第一個屬性(即使你真的不能訪問每類虛函數表的一個實例它自己)

現在,在多重繼承的情況下會發生什麼?那麼,繼承是非常像的存儲器佈局術語組合物:

|          Derived         | 
|     BaseA     |     BaseB     | 
| vpointer | field1 | field2 | padding? | vpointer | field1 | field2 | padding? | 

有將因此成爲MostDerived 2個虛擬表:一個以改變從BaseA和一個方法來從BaseB改變方法。

純虛函數通常表示爲一個空指針(簡單地)在相應的字段中。

最後,建築和破壞:

施工

  • BaseA構造:首先vpointer被初始化,則屬性,則該構造的主體被執行
  • BaseB被構造:vpointer,attributes,body
  • Derived構造:替換vpointers(兩者),屬性,主體

破壞

  • Derived is destructed:析構函數的身體,破壞屬性,把基地vpointers回
  • BaseB被破壞:身體屬性
  • BaseA被破壞:身體屬性

我認爲它非常全面,如果一些C++大師在那裏可以檢查一下並檢查我哈哈,我會很高興沒有犯任何愚蠢的錯誤。另外,如果缺少某些東西,我很樂意添加它。

+0

真的很全面的答案!非常感謝! – Bruce 2010-07-05 13:33:17

+0

虛擬繼承期間會發生什麼?你如何解釋蝙蝠在虛擬繼承之後變成了(vpointer,Mammal,vpointer,WingedAnimal,Bat,Animal)? – Bruce 2010-07-05 15:02:38

+1

這裏的「蝙蝠」是一個錯誤(會導致無限遞歸)而忽略它。在虛擬繼承的情況下,虛擬繼承的類只能被一個(通過派生類最多)實例化,但顯然在「Mammal」和「WingedAnimal」部分之前。 – 2010-07-05 16:16:48

6

我不能,真的。本節試圖描述在使用虛擬方法表提供動態綁定(在多重繼承的情況下)的C++實現中應該做些什麼。

如果你沒有編譯器,我的建議是:不要打擾。閱讀您最喜愛的繼承,虛擬方法,多繼承和虛擬繼承的C++書籍。

另外,C++標準(IIRC)不要求使用vtable,它是一個實現細節。所以真的,不要打擾。

+0

我已經開始寫一個解釋vpointers的答案,但是你的答案可能會更好,除非他想讀一整本書。 – 2010-07-04 17:08:45

+0

@Fogh:這件事一直困擾着我很長一段時間......我不介意看整本書 – Bruce 2010-07-04 17:14:25

1

正如mkluwe所說,vpointers並不是真正的語言組成部分。但是,瞭解 實現技術可能會很有用,特別是在像C++這樣的低級語言中。

如果你真的想了解這一點,我會推薦Inside the C++ Object Model,這解釋了這一點以及許多其他的細節。

+0

@Fogh:非常感謝 – Bruce 2010-07-04 17:15:02