-
一般繼承(無虛函數覆蓋)
- 只有一個虛指針,指向一個虛表,虛函數按順序從祖先節點開始插入到虛表上。
- 字段按順序從祖先節點開始插入到對象內存上
-
一般繼承(有虛函數覆蓋)
- 只有一個虛指針,指向一個虛表,虛函數按順序從祖先節點開始,先查找是否有可以覆蓋的,如果有,覆蓋掉,否則插到最后。
- 字段按順序從祖先節點開始插入到對象內存上
-
多重繼承(有虛函數)
- 根據繼承的類的數量來一一創建各自內存塊,如繼承了A,B,C三個類,則先后創建ABC三塊,每一個均有自己的虛指針及字段,子類的虛函數分別遍歷三個虛指針指向的虛表查找是否產生覆蓋,如果有,則覆蓋,如果沒有則插入到在第一個父類的虛表里
- 每個類的字段按順序跟在各個類內存塊的虛指針后面。
-
重復繼承(鉆石模型)
-
此類型與多重繼承類似,先把最底層的子類所繼承的若干個間接父類(如B1, B2)當作一個整體,把它們先按單一繼承創建,再根據多重繼承的性質創建。
-
-
-
-
鉆石型多重虛擬繼承
-
class B {……};class B1 : virtual public B{……};class B2 : virtual public B{……};class D : public B1, public B2{ …… };
- 虛擬繼承的就是為了解決重復繼承中多個間接父類的問題。大體思想是:先從間接父類開始創建,把頂層的超類放在內存模型的最后,同時間接父類的虛表只能含有本身的虛函數(同時不能包含頂層超類的虛函數)和子類的虛函數。
- 間接父類每一個虛指針后緊一個vbptr虛基類表指針,變量指向一個全類共享的偏移量表,表中項目記錄了對于該類而言,“虛基類表指針”與虛基類之間的偏移量。
- 內存模型創建過程:
- 先創建間接父類B1, 虛繼承先創建虛指針,緊跟vbptr,接著是成員變量;遍歷B1的虛函數,檢查B類中是否有,如果有,則跳過;如果沒有,則插入到函數表里。此時B1內存塊為:vptr, vbptr, ib1, cb1; 虛函數表有:B1::f1, B1::Bf1;
- 重復上面步驟繼續創建B2,此時內存塊為:vptr, vbptr, ib1, cb1, vptr, vbptr, ib2, cb2; B2虛函數表有:B2::f2, B2::Bf2;
- 創建完間接父類,繼而創建子類。把子類的字段按順序緊放在內存后面,子類的函數在間接父類中接按順序查找是否產生覆蓋(同時不能包含頂層超類的虛函數),如果有,則覆蓋,繼續下一個函數。例如先遍歷D::f,由于頂層超類包含了,跳過不處理;對于D::f1,在B1的虛函數表里找到了,則覆蓋,此時B1的虛函數表為:D::f1, B1::Bf1。對于D::f2,覆蓋了B2的B::f2,B2虛函數表有:D::f2, B2::Bf2; 對于D::Df,沒有找到覆蓋的,則插到第一個間接父類的虛函數表里。此時B1的虛函數表為:D::f1, B1::Bf1,D::Df。
- 最后創建頂層超類。按常規創建B類,創建完之后,用D類的虛函數遍歷檢查是否產生覆蓋,如果有,則覆蓋,否則跳過。
參考內容:
鉆石型繼承模型的內存分布
C++ 對象的內存布局 -