问题代码
#include <iostream> using namespace std; class base { public: base() {} virtual void show() { cout << "base:: show"<<endl; } private: int ma; }; class derive:virtual public base { public: derive() {} virtual void show() { cout << "derive:: show"; } private: int mb; }; int main() { cout << sizeof(derive) << endl; }
以上代码类大小常规来说应该是如下,占16个字节大小,但是为何是20呢
预期结构
class derive size(16): +--- 0 | {vbptr} 4 | mb +--- +--- (virtual base base) 8 | {vfptr} 12 | ma +---
实际结构,占大小20个字节
class derive size(20): +--- 0 | {vbptr} 4 | mb +--- 8 | (vtordisp for vbase base) +--- (virtual base base) 12 | {vfptr} 16 | ma +--- derive::$vbtable@: 0 | 0 1 | 12 (derived(derive+0)base) derive::$vftable@: | -12 0 | &(vtordisp) derive::show derive::show this adjustor: 12 vbi: class offset o.vbptr o.vbte fVtorDisp base 12 0 4 1
经过验证 必须满足以下两个条件
- 派生类重写了虚基类的虚函数。
- 派生类定义了构造函数或者析构函数。
才会产生vtordisp.
这就牵扯到vtordisp了,介绍如下
1. 基本概念回顾
在 C++ 中,当涉及虚继承时,为了确保在派生类对象中能正确定位虚基类的子对象(包含虚基类的数据成员、虚函数等内容),编译器会在派生类对象的内存布局中安排虚基类表指针等相关结构来记录偏移量信息,以实现准确访问虚基类部分。同时,对于有虚函数的类,会存在虚函数表(VTable)来支持多态调用。
而 vtordisp
主要用于处理虚继承和虚函数结合场景下,构造函数和析构函数中对虚基类指针调整的一种机制,它本质上是编译器为了正确处理对象的初始化和析构顺序、保证虚基类相关操作的正确性而引入的一个额外的字节(在 32 位系统下通常是 4 字节,64 位系统下通常是 8 字节)来存储偏移量相关信息。
2. 应用场景
虚继承与虚函数并存的类层次结构
- 场景描述:当类层次结构中既有虚继承又有类自身包含虚函数的情况时,在派生类的构造函数和析构函数中,需要准确地处理与虚基类相关的初始化和清理工作,同时还要考虑虚函数机制带来的多态性影响。
3. 编译器相关考虑
如果不想要vtordisp 可以加 #pragma vtordisp(off) 进行关闭。
vtordisp是Visual C++编译器的一个特性,主要用于解决在类继承中,虚函数的调用与对象布局的问题。当你在类中使用了虚函数,并且该类被继承,且继承类覆盖了基类的虚函数时,可能会出现所谓的”跳跃问题”(slicing problem)。
“跳跃问题”是指当你有一个基类的指针指向派生类对象,并且调用了虚拟函数,预期是派生类的函数被调用,但实际上可能会调用基类的函数。这是因为编译器为了能够快速地调用虚拟函数,直接使用了指针的偏移量来计算虚拟函数的地址,而不是检查实际对象的类型。
为了解决这个问题,当你在类中有一个或多个虚拟函数,并且类被继承,且派生类覆盖了基类的虚函数时,编译器可能会为类添加一个额外的隐藏成员,称为vtordisp字段。vtordisp字段的作用是在构造函数和析构函数执行过程中,记录对象的实际类型信息,以便正确地调用虚拟函数。具体由编译器进行管理。
在网上搜集资料并未搞清楚其底层原理,请知道的大佬不吝赐教。
到此这篇关于C++ vtordisp的应用场景的文章就介绍到这了,更多相关C++ vtordisp应用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
来源链接:https://www.jb51.net/program/333936a2w.htm
如有侵犯您的版权,请及时联系3500663466#qq.com(#换@),我们将第一时间删除本站数据。
暂无评论内容