測驗一、虛繼承與繼承的區別
1.1 單個繼承,不帶虛函式 1>class B size(8): 1> +--- 1> 0 | +--- (base class A) 1> 0 | | _ia //4B 1> | +--- 1> 4 | _ib //4B
有兩個int型別資料成員,占8B,基類邏輯存在前面

1.2、單個虛繼承,不帶虛函式 1>class B size(12): 1> +--- 1> 0 | {vbptr} //虛基指標(指向虛基表) 1> 4 | _ib //派生類放到前面 1> +--- 1> +--- (virtual base A) //虛基類 1> 8 | _ia 1> +--- 1>B::$vbtable@: //虛基表 1> 0 | 0 // 虛基指標距離派生類物件偏移0B 1> 1 | 8 (Bd(B+0)A) // 虛基指標向下偏移8B找到虛基類
虛繼承多一個虛基指標,共12B,虛擬繼承會將派生類的邏輯存到前面;
虛基表中存放的內容:(1)虛基指標距離派生類物件首地址的偏移資訊(2)虛基類的偏移資訊

測驗二、單個虛繼承,帶虛函式
2.1、單個繼承,帶虛函式 1>class B size(12): 1> +--- 1> 0 | +--- (base class A) 1> 0 | | {vfptr} //虛函式指標 1> 4 | | _ia 1> | +--- 1> 8 | _ib 1> +--- 1>B::$vftable@: //虛表 1> | &B_meta 1> | 0 1> 0 | &B::f // f 和 fb2 入虛表,fb不是虛函式,不入虛表 1> 1 | &B::fb2 // 派生類新增虛函式直接放在基類虛表中
帶虛函式的話,多一個虛函式指標,指向虛表,所以共占12B,派生類新增的虛函式放入基類虛表

2.3、單個虛繼承,帶虛函式,派生類不新增 8/16 1>class B size(16): 1> +--- 1> 0 | {vbptr} //有虛繼承的時候就多一個虛基指標,虛基指標指向虛基表 1> 4 | _ib //有虛函式的時候就產生一個虛函式指標,虛函式指標指向虛函式表 1> +--- 1> +--- (virtual base A) 1> 8 | {vfptr} 1>12 | _ia 1> +--- 1>B::$vbtable@: //虛基表 1> 0 | 0 // 虛基指標距離派生類物件偏移0B 1> 1 | 8 (Bd(B+0)A) // 虛基指標向下偏移8B找到虛基類 1>B::$vftable@: //虛函式表 1> | -8 1> 0 | &B::f
兩個 int 型變數,一個虛函式指標,一個虛基指標,共占16B;
虛擬繼承使得派生類邏輯存在基類前面;
(虛擬繼承后,基類在派生類后面,虛函式指標也在下面,派生類要找到虛函式表,向后偏移8B)

2.2 單個虛繼承,帶虛函式 (自己新增) 1>class B size(20): 1> +--- 1> 0 | {vfptr} //虛函式指標 1> 4 | {vbptr} //虛基指標 (虛繼承多一個) {虛擬繼承,派生類在前面} 1> 8 | _ib 1> +--- 1> +--- (virtual base A) 1>12 | {vfptr} //虛函式指標 1>16 | _ia 1> +--- 1>B::$vftable@B@: //虛表 1> | &B_meta 1> | 0 1> 0 | &B::fb2 //派生類新增虛函式,放在最前面,訪問新增虛函式快一些,不用偏移 ,多一個虛函式指標,指向新的虛表 1>B::$vbtable@: //虛基表 1> 0 | -4 //虛基指標距離派生類物件首地址的偏移資訊 1> 1 | 8 (Bd(B+4)A) //找到虛基類的偏移資訊 1>B::$vftable@A@: //虛表 1> | -12 1> 0 | &B::f 基類布局在最后面
派生類中新增一個虛函式指標,指向一張新的虛表,存放派生類新增的虛函式,可以更快的訪問到
所以,兩個虛函式指標,一個虛基指標,兩個int型別變數,共20B

測驗三:多重繼承(帶虛函式)
3.1、普通多重繼承,帶虛函式,自己有新增虛函式 28 //Base1中 f() g() h() , Base2中 f() g() h() , Base3中 f() g() h() Derived 中 f() g1() 1>class Derived size(28): 1> +--- 1> 0 | +--- (base class Base1) //基類有自己的虛函式表,基類的布局按照被繼承時的順序排列 1> 0 | | {vfptr} // 3個虛函式指標指向不同虛表 1> 4 | | _iBase1 1> | +--- 1> 8 | +--- (base class Base2) 1> 8 | | {vfptr} 1>12 | | _iBase2 1> | +--- 1>16 | +--- (base class Base3) 1>16 | | {vfptr} 1>20 | | _iBase3 1> | +--- 1>24 | _iDerived 1> +--- 1>Derived::$vftable@Base1@: 1> | &Derived_meta 1> | 0 1> 0 | &Derived::f(虛函式的覆寫) //第一個虛函式表中存放真實的被覆寫的虛函式地址,其他虛函式表中存放跳轉地址 1> 1 | &Base1::g 1> 2 | &Base1::h 1> 3 | &Derived::g1 (新的虛函式,直接放在基類之后,加快查找速度) 1>Derived::$vftable@Base2@: 1> | -8 1> 0 | &thunk: this-=8; goto Derived::f //虛函式表還可以存放跳轉指令 1> 1 | &Base2::g 1> 2 | &Base2::h 1>Derived::$vftable@Base3@: 1> | -16 1> 0 | &thunk: this-=16; goto Derived::f 1> 1 | &Base3::g 1> 2 | &Base3::h
Base1、Base2、Base3中各有一個虛函式指標指向自己的虛表,有4個int型別的資料成員,共占28B
第一個虛函式表中存放真實的被覆寫的虛函式地址,其他虛函式表中存放跳轉地址

3.2、虛擬多重繼承,帶虛函式,自己有新增虛函式(只有第一個是虛繼承) 32 Base1是虛繼承 1>class Derived size(32): //多一個虛基指標 1> +--- 1> 0 | +--- (base class Base2) 1> 0 | | {vfptr} 1> 4 | | _iBase2 1> | +--- 1> 8 | +--- (base class Base3) 1> 8 | | {vfptr} 1>12 | | _iBase3 1> | +--- 1>16 | {vbptr} 1>20 | _iDerived 1> +--- 1> +--- (virtual base Base1) 1>24 | {vfptr} 1>28 | _iBase1 1> +--- 1>Derived::$vftable@Base2@: 1> | &Derived_meta 1> | 0 1> 0 | &Derived::f //第一個虛函式表中存放真實的被覆寫的虛函式地址,其他虛函式表中存放跳轉地址 1> 1 | &Base2::g 1> 2 | &Base2::h 1> 3 | &Derived::g1 1>Derived::$vftable@Base3@: 1> | -8 //去找Derived::f 1> 0 | &thunk: this-=8; goto Derived::f 1> 1 | &Base3::g 1> 2 | &Base3::h 1>Derived::$vbtable@: //虛基表 1> 0 | -16 1> 1 | 8 (Derivedd(Derived+16)Base1) 1>Derived::$vftable@Base1@: 1> | -24 1> 0 | &thunk: this-=24; goto Derived::f 1> 1 | &Base1::g 1> 2 | &Base1::h
虛擬繼承會將派生類的邏輯存到前面,Base1是虛繼承,所以記憶體中的存放順序為 Base2、Base3、Derived、Base1
所占空間大小,在上面一個例子基礎上,多一個虛基指標,所以占32B
虛基指標向上偏移16B得到派生類物件首地址,向下偏移8B找到虛基類

3.3、虛擬多重繼承,帶虛函式,自己有新增虛函式(三個都是虛繼承) 36 1>class Derived size(36): //多一張虛表 1> +--- 1> 0 | {vfptr} //以空間換時間 新增虛函式,多張虛表 1> 4 | {vbptr} 1> 8 | _iDerived 1> +--- 1> +--- (virtual base Base1) 1>12 | {vfptr} 1>16 | _iBase1 1> +--- 1> +--- (virtual base Base2) 1>20 | {vfptr} 1>24 | _iBase2 1> +--- 1> +--- (virtual base Base3) 1>28 | {vfptr} 1>32 | _iBase3 1> +--- 1>Derived::$vftable@Derived@: 1> | &Derived_meta 1> | 0 1> 0 | &Derived::g1 1>Derived::$vbtable@: 1> 0 | -4 1> 1 | 8 (Derivedd(Derived+4)Base1) //vbptr偏移8B找到虛基類Base1 1> 2 | 16 (Derivedd(Derived+4)Base2) // vbptr偏移16B找到虛基類Base2 1> 3 | 24 (Derivedd(Derived+4)Base3) 1>Derived::$vftable@Base1@: 1> | -12 1> 0 | &Derived::f 1> 1 | &Base1::g 1> 2 | &Base1::h 1>Derived::$vftable@Base2@: 1> | -20 1> 0 | &thunk: this-=8; goto Derived::f 1> 1 | &Base2::g 1> 2 | &Base2::h 1>Derived::$vftable@Base3@: 1> | -28 1> 0 | &thunk: this-=16; goto Derived::f 1> 1 | &Base3::g 1> 2 | &Base3::h
虛擬繼承會將派生類的邏輯存到前面,3個Base都是虛繼承,所以記憶體中的存放順序為Derived、Base1、 Base2、Base3
在上一個例子的基礎上,多一張虛表,所以占36B

測驗四、菱形虛繼承
4.1、菱形普通繼承(存盤二義性) 48 B1、B2繼承B;D繼承B1、B2 class D size(48): 1> +--- 1> 0 | +--- (base class B1) 1> 0 | | +--- (base class B) 1> 0 | | | {vfptr} 1> 4 | | | _ib //存盤二義性 1> 8 | | | _cb //1 1> | | | <alignment member> (size=3) //記憶體對齊 1> | | +--- 1>12 | | _ib1 1>16 | | _cb1 1> | | <alignment member> (size=3) 1> | +--- 1>20 | +--- (base class B2) 1>20 | | +--- (base class B) 1>20 | | | {vfptr} 1>24 | | | _ib //存盤二義性 1>28 | | | _cb 1> | | | <alignment member> (size=3) 1> | | +--- 1>32 | | _ib2 1>36 | | _cb2 1> | | <alignment member> (size=3) 1> | +--- 1>40 | _id 1>44 | _cd 1> | <alignment member> (size=3) 1> +--- 1>D::$vftable@B1@: 1> | &D_meta 1> | 0 1> 0 | &D::f 1> 1 | &B::Bf 1> 2 | &D::f1 1> 3 | &B1::Bf1 1> 4 | &D::Df 1>D::$vftable@B2@: 1> | -20 1> 0 | &thunk: this-=20; goto D::f 1> 1 | &B::Bf 1> 2 | &D::f2 1> 3 | &B2::Bf2
B的資料成員有兩份,造成了存盤二義性,共占48B

4.2、菱形虛擬繼承 B1、B2虛擬繼承B;D普通繼承B1、B2 52 1>class D size(52): 1> +--- 1> 0 | +--- (base class B1) //基類B1 1> 0 | | {vfptr} 1> 4 | | {vbptr} // +36 找到虛基類 1> 8 | | _ib1 1>12 | | _cb1 1> | | <alignment member> (size=3) 1> | +--- 1>16 | +--- (base class B2) //基類B2 1>16 | | {vfptr} 1>20 | | {vbptr} // +20找到虛基類 1>24 | | _ib2 1>28 | | _cb2 1> | | <alignment member> (size=3) 1> | +--- 1>32 | _id //派生類D 1>36 | _cd 1> | <alignment member> (size=3) 1> +--- 1> +--- (virtual base B) //基類B 1>40 | {vfptr} 1>44 | _ib 1>48 | _cb 1> | <alignment member> (size=3) 1> +--- 1>D::$vftable@B1@: 1> | &D_meta 1> | 0 1> 0 | &D::f1 // D中覆寫了 1> 1 | &B1::Bf1 //新增 1> 2 | &D::Df //D中新增,放到B1的虛函式表中 1>D::$vftable@B2@: 1> | -16 1> 0 | &D::f2 // D中覆寫了 1> 1 | &B2::Bf2 //新增 1>D::$vbtable@B1@: 1> 0 | -4 //距離派生類物件B1首地址偏移 -4 1> 1 | 36 (Dd(B1+4)B) 1>D::$vbtable@B2@: 1> 0 | -4 //距離派生類物件B2首地址偏移 -4 1> 1 | 20 (Dd(B2+4)B) 1>D::$vftable@B@: 1> | -40 1> 0 | &D::f 1> 1 | &B::Bf
B1、B2各有虛基指標
存盤順序本來是:派生類B1、基類B、派生類B2、基類B、派生類D
存盤順序:派生類B1、派生類B2、派生類D、基類B(基類放到后面,解決了存盤二義性)

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/542419.html
標籤:其他
上一篇:day10-AOP-03
