c++虚表(vftable)、虚函数指针(vfptr)、虚基指针(vbptr)的测试结果
在VS中 --> 项目 --> 项目属性 --> C/C++ --> 命令行
添加编译选项
/d1reportSingleClassLayoutB
(B是你要查看的类名)
在GCC下测试 vfptr 和 vbptr 会合并,在windows下是分开的
代码一:测试虚标的存在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | /// /// @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>
代码二:多重继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | /// /// @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>
代码三:钻石型虚继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | /// /// @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)
(具体细节待补充)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】