菱形繼承(鑽石繼承)指兩個派生類繼承同一基類,再由底層類繼承這兩個派生類。若無虛繼承,底層類會包含兩份基類副本,導致數據冗余與二義性:
class B { int b; };
class C1 : public B { int c1; };
class C2 : public B { int c2; };
class D : public C1, public C2 { int d; };
sizeof(D) = 20(含兩個 b),訪問 b 會報「ambiguous access」。
使用 virtual public B 使基類在繼承體系中共享單一實例。編譯器透過 vbptr(虛基類表指針) 實現共享:
[this - vbptr, base - vbptr]sizeof(B)=1(佔位符),sizeof(C1)=sizeof(C2)=4(單一 vbptr),sizeof(D)=12(兩個 vbptr + 共享的 B):
D 對象佈局: [vbptr_C1] [C1 無數據] [vbptr_C2] [C2 無數據] [D 無數據] [B 共享部分]
每個 vbptr 指向各自的虛基類表,表中記錄偏移量定位共享的 B。
B{int b}、C1{int c1}、C2{int c2}、D{int d}:
sizeof(B)=4,sizeof(C1)=sizeof(C2)=12(vbptr + c1/c2 + 共享 B)sizeof(D)=24:[vbptr1][c1][vbptr2][c2][d][b]虛基類表偏移量動態計算:
第一個 vbptr 表:[0, 14](B 相對 vbptr1 偏移 14)
第二個 vbptr 表:[0, 12](B 相對 vbptr2 偏移 12)
本文不涉及虛函數多態情況,後續將分析帶虛函數的菱形繼承模型。
