天天看點

菱形繼承的内部實作方式

問題:

    由于将下圖定義為多繼承類型時,子類會發生二義性與資料備援,而用菱形繼承時會解決這些問題,菱形繼承發生了些什麼?又是怎麼實作的?

本次試着說明菱形繼承的機理(實作方法)

菱形繼承的内部實作方式

按照上圖建立多繼承,編寫代碼:

多繼承運作結果:(虛表指針位址可由運作時“d”的_vfptr可得)

菱形繼承的内部實作方式
菱形繼承的内部實作方式

可看出:Base1、Base2中都有func1();Base1的_vfptr與Base2的_vfptr位址不同,指向的内容也不同Base1的虛表與Base2的虛表都含有Base的func(),這種繼承存在二義性與備援性。

在定義 Base1,Base2時,在public Base前加 virtual,将此繼承變為菱形繼承。

菱形繼承運作結果:

菱形繼承的内部實作方式

菱形繼承解決了二義性與備援性問題。

多繼承計算大小得:

菱形繼承的内部實作方式

菱形繼承計算大小得:

菱形繼承的内部實作方式

将多繼承與菱形繼承結合起來分析:

菱形繼承的内部實作方式

    由上圖可以看出,在菱形繼承與多繼承時,超類大小一樣,但從父類開始大小發生差別,父類多了8個位元組,子類多了18個位元組。這其中做了些什麼?

    由于要想消除二義性與備援性,就得将Base1、Base2中的Base部分變為一份,那隻能将Base1、Base2中Base部分變為指針指向Base部分。那具體又是怎麼實作的?     

打開“d”的内部

菱形繼承的内部實作方式

發現多了一個Base,再将Base1、Base2、Base都打開。

菱形繼承的内部實作方式

看到:Base1的_vfptr,Base2的_vfptr,Base的_vfptr位址相同,指向的内容也一樣是Base的虛表。

菱形繼承的内部實作方式

通過上圖及調用記憶體視窗,對相應位址進行分析得到下圖

    由上圖分析可以得到:相比多繼承,菱形繼承中在子類中會多8個位元組(兩個指針),是因為在子類繼承的父類部分會各增加一個指針,作用是指向一個位址,位址中儲存着父類增加的指針的位址與超類的位址偏移值,通過位址與偏移值相加,找到超類成員部分,并且兩個父類指針都指向的是同一塊空間。

    這樣子類中父類與超類公共部分都是同一塊存儲空間,就解決了二義性與資料備援問題。