在VS中 --> 項目 --> 項目屬性 --> C/C++ --> 命令行
添加編譯選項
/d1reportSingleClassLayoutB
(B是你要查看的類名)
在GCC下測試 vfptr 和 vbptr 會合並,在windows下是分開的
代碼一:測試虛標的存在
/// /// @filename /// @author whao Luo /// @email haohb13@gmail.com /// @date /// #if 0 //測試虛表的存在 #include <iostream> using namespace std; class A { int i = 10; int ia = 100; void func() {} virtual void run() { cout << "A::run()" << endl; } virtual void run1() { cout << "A::run1()" << endl; } virtual void run2() { cout << "A::run2()" << endl; } }; class B : public A { virtual void run() { cout << "B::run()" << endl; } virtual void run1() { cout << "B::run1()" << endl; } }; class C :public A { virtual void run() { cout << "C::run()" << endl; } virtual void run1() { cout << "C::run1()" << endl; } virtual void run3() { cout << "C::run3()" << endl; } }; class D :/*virtual*/ public A { virtual void run() { cout << "D::run()" << endl; } virtual void run1() { cout << "D::run1()" << endl; } virtual void run2() { cout << "D::run2()" << endl; } virtual void run3() { cout << "D::run3()" << endl; } }; int test() { cout << sizeof(A) << endl << sizeof(B) << endl << sizeof(C) << endl << sizeof(D) << endl; cout << sizeof(long long) << endl; //A * pA = new D; D d; //d.run(); typedef void(*Function)(void); int ** pVtable = (int **)&d; #if 0 int * pVtable = (int*)&d; int vtaleAdress = *pVtable; int * ppVtable = (int*)vtaleAdress; int func1 = *ppVtable; Function f1 = (Function)func1; f1() #endif //pVtable[0][0] for (int idx = 0; pVtable[0][idx] != NULL; ++idx) { Function f = (Function)pVtable[0][idx]; f(); } //cout << (int)pVtable[1] << endl; //cout << (int)pVtable[2] << endl; getchar(); return 0; } int main(void) { test(); return 0; } #endif #if 0 // 測試一:單個虛繼承,不帶虛函數 // 虛繼承與繼承的區別 // 1. 多了一個虛基指針 // 2. 虛基類位於派生類存儲空間的最末尾 // 測試二:單個虛繼承,帶虛函數 // 1.如果派生類沒有自己的虛函數,此時派生類對象不會產生 // 虛函數指針 // 2.如果派生類擁有自己的虛函數,此時派生類對象就會產生自己本身的虛函數指針, // 並且該虛函數指針位於派生類對象存儲空間的開始位置 // #pragma vtordisp(off) #include <iostream> using std::cout; using std::endl; class A { public: A() : _ia(10){} virtual void f() { cout << "A::f()" << endl; } private: int _ia; }; class B : /*virtual*/ public A { public: B() : _ib(20){} void fb() { cout << "A::fb()" << endl; } virtual void f() { cout << "B::f()" << endl; } #if 1 virtual void fb2() { cout << "B::fb2()" << endl; } #endif private: int _ib; }; int main(void) { cout << sizeof(A) << endl; cout << sizeof(B) << endl; B b; getchar(); return 0; } #endif
測試一:不帶虛繼承,不帶虛函數
結果:內存空間只有變量
1>class B size(8):
1> +---
1> 0 | +--- (base class A)
1> 0 | | _ia
1> | +---
1> 4 | _ib
1> +---
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
1> 1 | &B::fb2
測三:單個虛繼承,不帶虛函數
結果:虛繼承與繼承的區別
1. 多了一個虛基指針
2. 虛基類位於派生類存儲空間的最末尾
1>class B size(12):
1> +---
1> 0 | {vbptr} --> 虛基指針
1> 4 | _ib
1> +---
1> +--- (virtual base A)
1> 8 | _ia
1> +---
測試四:單個虛繼承,帶虛函數
結果一:.如果派生類沒有獨立的虛函數,此時派生類對象不會產生 虛函數指針
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::$vftable@:
1> | -8
1> 0 | &B::f
1>
結果二:如果派生類擁有自己的虛函數,此時派生類對象就會產生自己本身的虛函數指針,並且該虛函數指針位於派生類對象存儲空間的最開始位置(放在了虛基指針的前面,為了加快虛函數的查找速度)
1>class B size(20):
1> +---
1> 0 | {vfptr} // 在GCC中測試時,vfptr和vbptr會合並
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::$vftable@A@:
1> | -12
1> 0 | &B::f
1>
代碼二:多重繼承
/// /// @filename /// @author whao Luo /// @email haohb13@gmail.com /// @date /// // 測試三:多重繼承(帶虛函數) // 1. 每個基類都有自己的虛函數表 // 2. 派生類如果有自己的虛函數,會被加入到第一個虛函數表之中 // 3. 內存布局中, 其基類的布局按照基類被聲明時的順序進行排列 // 4. 派生類會覆蓋基類的虛函數,只有第一個虛函數表中存放的是 // 真實的被覆蓋的函數的地址;其它的虛函數表中存放的並不是真實的 // 對應的虛函數的地址,而只是一條跳轉指令 #if 0 #pragma vtordisp(off) #include <iostream> using std::cout; using std::endl; class Base1 { public: Base1() : _iBase1(10){} virtual void f() { cout << "Base1::f()" << endl; } virtual void g() { cout << "Base1::g()" << endl; } virtual void h() { cout << "Base1::h()" << endl; } private: int _iBase1; }; class Base2 { public: Base2() : _iBase2(100){} virtual void f() { cout << "Base2::f()" << endl; } virtual void g() { cout << "Base2::g()" << endl; } virtual void h() { cout << "Base2::h()" << endl; } private: int _iBase2; }; class Base3 { public: Base3() : _iBase3(1000){} virtual void f() { cout << "Base3::f()" << endl; } virtual void g() { cout << "Base3::g()" << endl; } virtual void h() { cout << "Base3::h()" << endl; } private: int _iBase3; }; class Derived : virtual public Base1 , public Base2 , public Base3 { public: Derived() : _iDerived(10000){} void f() { cout << "Derived::f()" << endl; } virtual void g1() { cout << "Derived::g1()" << endl; } private: int _iDerived; }; int main(void) { Derived d; Base2 * pBase2 = &d; Base3 * pBase3 = &d; Derived * pDerived = &d; pBase2->f(); cout << "sizeof(d) = "<< sizeof(d) << endl; cout << "&Derived = " << &d << endl; // 這三個地址值是不一樣的 cout << "pBase2 = " << pBase2 << endl; // cout << "pBase3 = " << pBase3 << endl; // getchar(); return 0; } #endif
測試五:多重繼承(帶虛函數)
結果:
1. 每個基類都有自己的虛函數表
2. 派生類如果有自己的虛函數,會被加入到第一個虛函數表之中
3. 內存布局中, 其基類的布局按照基類被聲明時的順序進行排列
4. 派生類會覆蓋基類的虛函數,只有第一個虛函數表中存放的是真實的被覆蓋的函數的地址;其它的虛函數表中存放的並不是真實的對應的虛函數的地址,而只是一條跳轉指令
1>class Derived size(28):
1> +---
1> 0 | +--- (base class Base1)
1> 0 | | {vfptr}
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>
1>Derived::$vftable@Base2@:
1> | -8
1> 0 | &thunk: this-=8; goto Derived::f 跳轉指令
1> 1 | &Base2::g
1> 2 | &Base2::h
1>
1>Derived::$vftable@Base3@:
1> | -16
1> 0 | &thunk: this-=16; goto Derived::f
1> 1 | &Base3::g
1> 2 | &Base3::h
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>
1>Derived::$vftable@Base3@:
1> | -8
1> 0 | &thunk: this-=8; goto Derived::f
1> 1 | &Base3::g
1> 2 | &Base3::h
1>Derived::$vftable@Base1@:
1> | -24
1> 0 | &thunk: this-=24; goto Derived::f
1> 1 | &Base1::g
1> 2 | &Base1::h
1>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> | | | <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>
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
1>
代碼三:鑽石型虛繼承
/// /// @filename /// @author whao Luo /// @email haohb13@gmail.com /// @date /// // 測試四:鑽石型虛繼承 //虛基指針所指向的虛基表的內容: // 1. 虛基指針的第一條內容表示的是該虛基指針距離所在的子對象的首地址的偏移 // 2. 虛基指針的第二條內容表示的是該虛基指針距離虛基類子對象的首地址的偏移 #if 1 #pragma vtordisp(off) #include <iostream> using std::cout; using std::endl; class B { public: B() : _ib(10), _cb('B'){} virtual void f() { cout << "B::f()" << endl; } virtual void Bf() { cout << "B::Bf()" << endl; } private: int _ib; char _cb; }; class B1 : virtual public B { public: B1() : _ib1(100), _cb1('1'){} virtual void f() { cout << "B1::f()" << endl; } #if 1 virtual void f1() { cout << "B1::f1()" << endl; } virtual void Bf1() { cout << "B1::Bf1()" << endl; } #endif private: int _ib1; char _cb1; }; class B2 : virtual public B { public: B2() : _ib2(1000), _cb2('2'){} virtual void f() { cout << "B2::f()" << endl; } #if 1 virtual void f2() { cout << "B2::f2()" << endl; } virtual void Bf2() { cout << "B2::Bf2()" << endl; } #endif private: int _ib2; char _cb2; }; class D : public B1, public B2 { public: D() : _id(10000), _cd('3'){} virtual void f() { cout << "D::f()" << endl; } #if 1 virtual void f1() { cout << "D::f1()" << endl; } virtual void f2() { cout << "D::f2()" << endl; } virtual void Df() { cout << "D::Df()" << endl; } #endif private: int _id; char _cd; }; int main(void) { D d; cout << sizeof(d) << endl; getchar(); return 0; } #endif
測試六:鑽石型虛繼承
虛基指針所指向的虛基表的內容:
1. 虛基指針的第一條內容表示的是該虛基指針距離所在的子對象的首地址的偏移
2. 虛基指針的第二條內容表示的是該虛基指針距離虛基類子對象的首地址的偏移
1>class D size(52):
1> +---
1> 0 | +--- (base class B1)
1> 0 | | {vfptr}
1> 4 | | {vbptr}
1> 8 | | _ib1
1>12 | | _cb1
1> | | <alignment member> (size=3)
1> | +---
1>16 | +--- (base class B2)
1>16 | | {vfptr}
1>20 | | {vbptr}
1>24 | | _ib2
1>28 | | _cb2
1> | | <alignment member> (size=3)
1> | +---
1>32 | _id
1>36 | _cd
1> | <alignment member> (size=3)
1> +---
1> +--- (virtual base B)
1>40 | {vfptr}
1>44 | _ib
1>48 | _cb
1> | <alignment member> (size=3)
1> +---
1>
1>D::$vftable@B1@:
1> | &D_meta
1> | 0
1> 0 | &D::f1
1> 1 | &B1::Bf1
1> 2 | &D::Df
1>
1>D::$vftable@B2@:
1> | -16
1> 0 | &D::f2
1> 1 | &B2::Bf2
1>D::$vftable@B@:
1> | -40
1> 0 | &D::f
1> 1 | &B::Bf
1>D::$vbtable@B1@:
1> 0 | -4
1> 1 | 36 (Dd(B1+4)B)
1>
1>D::$vbtable@B2@:
1> 0 | -4
1> 1 | 20 (Dd(B2+4)B)
(具體細節待補充)