两层结构时虚基类表内容分析
虚基类表内容之5-8字节内容分析
- 虚基类表 一般是8字节,四个字节为一个单位。每多一个虚基类,虚基类表会多加4个字节
class Grand //爷爷类 { public: int m_grand; }; class A1 : virtual public Grand { public: int m_a1; }; class A2 : virtual public Grand { public: int m_a2; }; class C1 :public A1, public A2 { public: int m_c1; }; int main() { cout << sizeof(Grand) << endl; cout << sizeof(A1) << endl; cout << sizeof(A2) << endl; cout << sizeof(C1) << endl; A1 a1obj; a1obj.m_grand = 2; a1obj.m_a1 = 5; return 1;
- 编译器因为有虚基类,会给A1,A2类增加默认的构造函数,并且这个默认构造函数里,会被编译器增加进去代码,给vbptr虚基类表指针赋值。
- 调试
- 上面反汇编代码中,先F10执行到A1::A1(),F11跳进函数体
虚基类中m_grand赋值情况探究
- a1obj模型
- 反汇编代码:
- 打开调试 -->窗口 -->寄存器
- EAX中存的是虚基类表指针
虚基类表中(虚基类表内容之5-8字节内容)记录着在子类(A1)模型中虚基类对象的偏移值
- “虚基类表指针”成员变量的首地址(vbptr地址) + 这个偏移量 就等于 虚基类对象首地址。跳过这个偏移值,我们就能够访问到虚基类对象。
class Grand //爷爷类 { public: int m_grand; }; class Grand2 //爷爷类 { public: int m_grand2; }; class A1 : virtual public Grand,public Grand2 { public: int m_a1; }; class A2 : virtual public Grand { public: int m_a2; }; class C1 :public A1, public A2 { public: int m_c1; }; int main() { cout << sizeof(Grand) << endl; cout << sizeof(A1) << endl; cout << sizeof(A2) << endl; cout << sizeof(C1) << endl; A1 a1obj; a1obj.m_grand = 2; a1obj.m_a1 = 5; return 1;
- 输出
- 调试
- 虚函数表内容
- 内存布局
- 将上面代码改为
... class A1 : public Grand,virtual public Grand2 { public: int m_a1; }; ...
- 调试
- 内存布局
- 将上面代码改为
... class A1 : virtual public Grand,virtual public Grand2 { public: int m_a1; }; ...
- 调试
- 内存布局
- 虚基类表内容
- 汇编
- 虚基类表 现在是3项, +4,+8,都是通过取得虚基类表中的偏移值来赋值的
- 虚基类表中的偏移量是按照继承顺序来存放的
- 虚基类子对象一直放在最下
虚基类表内容之1-4字节内容分析
- 虚基类表指针成员变量的首地址 ,和本对象A1首地址之间的偏移量 也就是:虚基类表指针 的首地址 - A1对象的首地址
- 结论:只有对虚基类成员进行处理比如赋值的时候,才会用到虚基类表,取其中的偏移,参与地址的计算