c ,為什麼要引入虛擬繼承

2021-09-08 11:14:34 字數 2232 閱讀 3252

虛擬基類是為解決多重繼承而出現的。

以下面的乙個例子為例:

#include

#include

class ca

}; class cb : public ca ;

class cc : public ca ;

class cd : public cb, public cc ;

void main()

error c2385: 'cd::f' is ambiguous

即編譯器無法確定你在d.f()中要呼叫的函式f到底是哪乙個。這裡可能會讓人覺得有些奇怪,命名只定義了乙個ca::f,既然大家都派生自ca,那自然就是呼叫的ca::f,為什麼還無法確定呢?

這是因為編譯器在進行編譯的時候,需要確定子類的函式定義,如ca::f是確定的,那麼在編譯cb、cc時還需要在編譯器的

語法樹中生成cb::f,cc::f等標識,那麼,在編譯cd的時候,由於cb、cc都有乙個函式f,此時,編譯器將試圖生成這兩個cd::f標識,顯然這時就要報錯了。(當我們不使用cd::f的時候,以上標識都不會生成,所以,如果去掉d.f()一句,程式將順利通過編譯)

要解決這個問題,有兩個方法:

1、重寫函式f():此時由於我們明確定義了cd::f,編譯器檢查到cd::f()呼叫時就無需再像上面一樣去逐級生成cd::f標識了;

此時cd的元素結構如下:

|cb(ca)|

|cc(ca)|

故此時的sizeof(cd) = 8;(cb、cc各有乙個元素k)

2、使用虛擬繼承:虛擬繼承又稱作共享繼承,這種共享其實也是編譯期間實現的,當使用虛擬繼承時,上面的程式將變成下面的形式:

#include

#include

class ca

}; class cb : virtual public ca //也有一種寫法是class cb : public virtual ca ;

class cc : virtual public ca ;

class cd : public cb, public cc ;

void main()

此時,當編譯器確定d.f()呼叫的具體含義時,將生成如下的cd結構:

|cb|

|cc|

|ca|

同時,在cb、cc中都分別包含了乙個指向ca的虛基類指標列表vbptr(virtual base table pointer)(虛基表指標),其中記錄的是從cb、cc的vbtable的首位址(vbptr)到ca的元素之間的偏移量。此時,不會生成各子類的函式f標識,除非子類重寫了該函式,從而達到「共享」的目的(這裡的具體記憶體布局,可以參看鑽石型繼承記憶體布局,在白楊的那篇文章中也有)(vs2010中,在project properties->c++->command line->additional options裡面加上/d1reportsingleclasslayoutx,可以檢視類x的物件布局)。

也正因此,此時的sizeof(cd) = 12(vbptrcb + vbptrcc + sizoef(int))(32位機中指標佔4個位元組);

另註:如果cb,cc中各定義乙個int型變數,則sizeof(cd)就變成20(兩個vbptr + 3個sizoef(int)

如果ca中新增乙個virtual void f1(){},sizeof(cd) = 16(vfptrca +vbptrcb + vbptrcc + sizoef(int));

再新增virtual void f2(){},sizeof(cd) = 16不變。原因如下所示:帶有虛函式(大於等於1個)的類,其記憶體布局上包含乙個指向虛函式列表的指標(vfptr)(虛函式表指標),這跟該類有幾個虛函式無關。

引用:

例項:

1. 多繼承的困惑:

#include #include 

#if 1

#define virtual virtual

#else

#define virtual

#endif

class

ca //不論基類的方法是否是虛函式,子類繼承後都會有他的例項。

};class cb : publicca;

class cc : publicca;

class cd : public cb, publiccc;

int main(void)/*

test.cpp:29: error: request for member 『f』 is ambiguous

*/

為什麼要引入補碼

現在我們知道了計算機可以有三種編碼方式表示乙個數.對於正數因為三種編碼方式的結果都相同,所以不需要過多解釋 原碼 1 0000 0001 反碼 1 0000 0001 補碼 1 0000 0001 為了解決原碼做減法的問題,出現了反碼 1 1 1 1 0000 0001 原 1000 0001 原 ...

為什麼要引入註解

使用annotation之前 甚至在使用之後 xml被廣泛的應用於描述元資料。不知何時開始一些應用開發人員和架構師發現xml的維護越來越糟糕了。他們希望使用一些和 緊耦合的東西,而不是像xml那樣和 是松耦合的 在某些情況下甚至是完全分離的 描述。如果你在google中搜尋 xml vs.annot...

C 為什麼要引入成員函式?

問題引入 程式設計提示使用者輸入圓的半徑,計算輸出圓的面積 include include intmain void 物件導向首先需要對問題進行抽象,定義乙個描述圓的資料型別,再建立具體的圓物件。在c語言中,可以定義乙個結構體struct,用來包含圓的半徑等資訊,而c 中稱為類class。類和結構體...