虛擬繼承

1.爲什麼需要虛繼承

如下圖所示如果訪問Der::Fun or Der::m_nValue就會帶來二義性,無法確定是調用Base1的還是Base2的,所以爲了解決多重繼承情況下成員訪問的二義性,引入了虛繼承機制。
一般繼承:
一般繼承

虛繼承:
虛繼承

2.虛繼承實現

在虛繼承下,Der通過共享虛基類SuperBase來避免二義性,在Base1,Base2中分別保存虛基類指針,Der繼承Base1,Base2,包含Base1, Base2的虛基類指針,並指向同一塊內存區,這樣Der便可以間接存取虛基類的成員,如下圖所示:

3.不同編譯器實現方式

不同編譯器對間接存取的方法不同,以下以GCC和VC爲例,均採用以下代碼進行實驗:

view plaincopy to clipboardprint?

  1. class SuperBase  
  2. {  
  3. public:  
  4.     int m_nValue;  
  5.     void Fun(){}  
  6.     virtual ~SuperBase(){}  
  7. };  
  8. class Base1:  virtual public SuperBase  
  9. {  
  10. public:  
  11. virtual ~ Base1(){}  
  12. };  
  13. class Base2:  virtual public SuperBase  
  14. {  
  15. public:  
  16. virtual ~ Base2(){}  
  17. };  
  18. class Der:public Base1, public Base2  
  19. {  
  20. public:  
  21. virtual ~ Der(){}  
  22. };  

1) GCC中結果爲8, 12, 12, 16

解析:sizeof(SuperBase) = sizeof(int) + 虛函數表指針

sizeof(Base1) = sizeof(Base2) = sizeof(int) + 虛函數指針 + 虛基類指針

sizeof(Der) = sizeof(int) + Base1中虛基類指針 + Base2虛基類指針 + 虛函數指針

GCC共享虛函數表指針,也就是說父類如果已經有虛函數表指針,那麼子類中共享父類的虛函數表指針空間,不在佔用額外的空間,這一點與VC不同,VC在虛繼承情況下,不共享父類虛函數表指針,詳見如下。

2)VC中結果爲:8, 16, 16, 24

解析:sizeof(SuperBase) = sizeof(int) + 虛函數表指針

sizeof(Base1) = sizeof(Base2) = sizeof(int) + SuperBase虛函數指針 + 虛基類指針 + 自身虛函數指針

sizeof(Der) = sizeof(int) + Base1中虛基類指針 + Base2中虛基類指針 + Base1虛函數指針 + Base2虛函數指針 + 自身虛函數指針

如果去掉虛繼承,結果將和GCC結果一樣,A,B,C都是8,D爲16,原因就是VC的編譯器對於非虛繼承,父類和子類是共享虛函數表指針的。

 

繼承中的內存佈局

1. 虛函數指針(vptr)放最前,之後放變量。
2. 多個父類排着放,再放子類
3. 子類的覆蓋的虛函數將所有祖先的同名虛函數都覆蓋。
4. 子類其它的虛函數指針放在第一個父類的虛函數表裏。
5. 虛擬繼承的情況只需要在鑽石繼承中有必要使用(避免二義性),子類中最先的祖先放最後。

多說不如舉例,看下面轉的文章。

本文章轉自:http://blog.csdn.net/randyjiawenjie/article/details/6693337

分爲四種情況:
1.單繼承

2.多繼承(不含鑽石繼承)

3.非虛繼承的鑽石繼承

4.虛繼承的鑽石繼承

注:下面所有類中的函數都是虛函數。

1.單繼承

單繼承體系如下:

GrandChild對象的內存佈局:

可見以下幾個方面:

1)虛函數表在最前面的位置。

2)成員變量根據其繼承和聲明順序依次放在後面。

3)在單一的繼承中,被overwrite的虛函數在虛函數表中得到了更新。

2.多繼承

多繼承的體系如下:

Derive對象的內存佈局如下:

我們可以看到:

1) 每個父類都有自己的虛表。

2) 子類的成員函數被放到了第一個父類的表中。

3) 內存佈局中,其父類佈局依次按聲明順序排列。

4) 每個父類的虛表中的f()函數都被overwrite成了子類的f()。這樣做就是爲了解決不同的父類類型的指針指向同一個子類實例,而能夠調用到實際的函數。

出現鑽石繼承的虛繼承的時候,虛基類在子類中只有一份。

出現鑽石繼承的非虛繼承的時候,虛基類在每個子類中都有一份。

3.非虛繼承的鑽石繼承

繼承體系如下:

D的內存佈局如下:

紅色的部分就是重複的部分,就會造成二義性

4.虛繼承的鑽石繼承

(虛繼承就是解決鑽石繼承問題的,如果不存在鑽石繼承,就不用虛繼承)

繼承體系如下:(紅色專門標準虛繼承)


D的內存佈局如下:

可以看出,少了重合的部分。但是,代價是增加了一個虛函數指針。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章