c++虚表(vftable)、虚函数指针(vfptr)、虚基指针(vbptr)的测试结果
2021-01-21 08:12
标签:存在 tchar 命令行 col date 跳转指令 ++ virtual file 测试一:不带虚继承,不带虚函数 结果:内存空间只有变量 1>class B size(8): 测试二:不带虚继承,基类带虚函数 结果:基类会带有一个虚函数指针 虚指针 1>class B size(12): 虚函数表 1>B::$vftable@: 测三:单个虚继承,不带虚函数 1>class B size(12): 测试四:单个虚继承,带虚函数 1>class B size(16): 1>B::$vftable@: 结果二:如果派生类拥有自己的虚函数,此时派生类对象就会产生自己本身的虚函数指针,并且该虚函数指针位于派生类对象存储空间的最开始位置(放在了虚基指针的前面,为了加快虚函数的查找速度) 1>class B size(20): 1>B::$vftable@B@: 1>B::$vftable@A@: 结果: 1>Derived::$vftable@Base1@: 1>D::$vftable@B1@: 虚基指针所指向的虚基表的内容: 1>D::$vftable@B@: (具体细节待补充) c++虚表(vftable)、虚函数指针(vfptr)、虚基指针(vbptr)的测试结果 标签:存在 tchar 命令行 col date 跳转指令 ++ virtual file 原文地址:https://www.cnblogs.com/cthon/p/12897719.html
在VS中 --> 项目 --> 项目属性 --> C/C++ --> 命令行
添加编译选项
/d1reportSingleClassLayoutB
(B是你要查看的类名) 代码一:测试虚标的存在
///
/// @filename
/// @author whao Luo
/// @email haohb13@gmail.com
/// @date
///
#if 0
//测试虚表的存在
#include
1> +---
1> 0 | +--- (base class A)
1> 0 | | _ia
1> | +---
1> 4 | _ib
1> +---
1>
1> +---
1> 0 | +--- (base class A)
1> 0 | | {vfptr}
1> 4 | | _ia
1> | +---
1> 8 | _ib
1> +---
1> | &B_meta
1> | 0
1> 0 | &B::f
1> 1 | &B::fb2
结果:虚继承与继承的区别
1. 多了一个虚基指针
2. 虚基类位于派生类存储空间的最末尾
1> +---
1> 0 | {vbptr} --> 虚基指针
1> 4 | _ib
1> +---
1> +--- (virtual base A)
1> 8 | _ia
1> +---
结果一:.如果派生类没有独立的虚函数,此时派生类对象不会产生 虚函数指针
1> +---
1> 0 | {vbptr}
1> 4 | _ib
1> +---
1> +--- (virtual base A)
1> 8 | {vfptr}
1>12 | _ia
1> +---
1> | -8
1> 0 | &B::f
1>
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_meta
1> | 0
1> 0 | &B::fb2
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
测试五:多重继承(带虚函数)
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> | -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> | | | (size=3)
1> | | +---
1>12 | | _ib1
1>16 | | _cb1
1> | | (size=3)
1> | +---
1>20 | +--- (base class B2)
1>20 | | +--- (base class B)
1>20 | | | {vfptr}
1>24 | | | _ib
1>28 | | | _cb
1> | | | (size=3)
1> | | +---
1>32 | | _ib2
1>36 | | _cb2
1> | | (size=3)
1> | +---
1>40 | _id
1>44 | _cd
1> | (size=3)
1> +---
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
测试六:钻石型虚继承
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> | | (size=3)
1> | +---
1>16 | +--- (base class B2)
1>16 | | {vfptr}
1>20 | | {vbptr}
1>24 | | _ib2
1>28 | | _cb2
1> | | (size=3)
1> | +---
1>32 | _id
1>36 | _cd
1> | (size=3)
1> +---
1> +--- (virtual base B)
1>40 | {vfptr}
1>44 | _ib
1>48 | _cb
1> | (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> | -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)
文章标题:c++虚表(vftable)、虚函数指针(vfptr)、虚基指针(vbptr)的测试结果
文章链接:http://soscw.com/index.php/essay/44896.html