没有使用虚继承情况下
#include<iostream>
class Base {
public:
int m_Age1; // 定义公共成员变量
};
class derived:public Base{
public:
int m_Age2;// 定义公共成员变量
};
int main() {
return 0;
}
derived 类的对象模型:
使用虚继承后
#include<iostream>
class Base {
public:
int m_Age1; // 定义公共成员变量
};
class derived:virtual public Base{
public:
int m_Age2;// 定义公共成员变量
};
int main() {
return 0;
}
derived 类的对象模型:
这幅图片展示了 C++ 代码的编译结果,分析的是一个 derived
类的内存布局以及虚表(vtable)和虚基表(vbtable)信息。以下是详细分析:
1. 类 derived
的内存布局
class derived size(20):
derived
类的大小为 20 字节,表明该类可能包含指针(通常为 8 字节,在 64 位系统上)和一些数据成员。
成员变量的布局
0 | {vbptr} // 虚基类指针(vbptr)
8 | m_Age2 // 普通成员变量
| <alignment member> (size=4) // 对齐填充
16 | m_Age1 // 虚基类 Base 的成员变量
偏移 0 处的
{vbptr}
这是 虚基类指针(vbptr),用于指向虚基类表(vbtable)。当一个类有虚基类时,编译器需要额外的指针来解析虚基类的偏移,从而支持动态多态。偏移 8 处的
m_Age2
这是derived
类自己的成员变量m_Age2
,紧随vbptr
之后。对齐填充(alignment member,size=4)
由于m_Age2
的大小可能导致对齐问题(例如,它可能是 4 字节,而后续成员需要 8 字节对齐),编译器插入 4 字节填充 以保证对齐。偏移 16 处的
m_Age1
这是Base
(虚基类)的成员变量m_Age1
,位于偏移 16 处,表明Base
在derived
类中的起始地址是 16。
2. 虚基表(vbtable)
derived::$vbtable@:
0 | 0
1 | 16 (derived(derived+0)Base)
vbtable
记录了虚基类 Base
在 derived
类中的偏移信息:
- 索引 0: 存储
0
,通常是用于虚拟基类解析的占位符。 - 索引 1: 存储
16
,表示Base
在derived
类中的偏移量是 16 字节。
3. 虚基表信息
vbi:
class offset o.vbptr o.vbte fVtorDisp
Base 16 0 4 0
- Base 作为虚基类
offset = 16
,表示Base
在derived
类中的实际偏移量。o.vbptr = 0
,表示Base
本身没有虚基类指针。o.vbte = 4
,表示Base
的虚基表条目在vbtable
中的偏移量。fVtorDisp = 0
,表示没有额外的构造/析构调整。
总结
derived
继承自Base
,且Base
是 虚基类,导致derived
需要vbptr
来存储虚基类偏移信息。derived
的内存布局为:- 8 字节用于
vbptr
m_Age2
在偏移 8- 4 字节的填充用于对齐
m_Age1
在偏移 16(属于虚基类Base
)
- 8 字节用于
vbtable
记录了Base
在derived
类中的偏移量(16)。- 由于虚基类的存在,
derived
类的大小 增加 了(否则可能会更小)。
这个输出通常由 clang
或 msvc
的 clang -fdump-record-layouts
或 dumpbin
生成,可用于分析 C++ 类的内存布局和虚拟继承机制的实现。
虚基类指针(vbptr)的归属
虚基类指针(
vbptr
)并不属于类本身,而是每个对象实例中的一部分。它用于在多继承和虚基类的情况下,帮助对象正确解析虚基类的存储位置。然而,即使没有实例化对象,编译器仍然能够静态分析类的内存布局,并报告
vbptr
的存在。这是因为:
类的内存布局在编译期就确定了
编译器需要为derived
计算所需的存储空间,并决定对象的内存分布,即便还没有实际分配对象。这个过程中,它会识别出Base
是derived
的虚基类,因此必须在derived
的实例中存储vbptr
。虚基类指针
vbptr
归属于类的实例,而不是类本身
vbptr
是对象的一部分,每个derived
的实例都会有自己的vbptr
,指向相应的vbtable
。- 但
vbtable
(虚基表)是类级别的数据结构,所有derived
的实例共享相同的vbtable
。