类对象大小由非静态数据成员、内存对齐填充、虚函数表指针(vfptr)、虚基表指针(vbptr)及空类特殊规则共同决定,静态成员和成员函数不参与计算。

类对象的大小不是成员变量简单相加,而是由编译器根据内存布局规则综合计算出来的结果。直接看 sizeof 输出值容易误判,关键得知道它到底在算什么。
非静态数据成员 + 内存对齐填充
这是最基础、最常被低估的部分。所有非静态成员变量的类型大小、声明顺序、平台默认对齐值(如 MSVC 默认 8,GCC 可能按最大成员对齐)共同决定实际占用。比如:
-
char a; int b;在 64 位下通常占 8 字节(a占 1,后面填 3,b占 4,末尾再填 0 —— 总大小需是最大成员对齐值 4 的倍数) - 但反过来写成
int b; char a;,末尾就得额外补 3 字节凑成 8 字节,否则sizeof仍是 8 - 若改成
double c; char a;,最大对齐值变成 8,总大小会是 16(c占 8,a占 1,后补 7)
虚函数带来 vfptr 指针开销
只要类中定义了至少一个虚函数(哪怕 virtual void f() = 0;),编译器就会为每个对象插入一个虚函数表指针(vfptr)。这个指针大小取决于平台:
- 32 位系统:固定 +4 字节
- 64 位系统:固定 +8 字节
- 注意:多个虚函数不增加额外指针,只共享这一个
vfptr - 继承链中只要任意一级有虚函数,派生类对象就含
vfptr(即使自己没新增虚函数)
虚继承引入 vbptr 指针
虚继承不是“多加一个基类子对象”,而是改用间接方式访问虚基类,因此需要额外的虚基表指针(vbptr)。它和 vfptr 是独立的:
立即学习“C++免费学习笔记(深入)”;
- 单虚继承:+1 个
vbptr(32 位 +4,64 位 +8) - 多重虚继承(如同时虚继承 A 和 B):可能 +2 个
vbptr(取决于编译器实现,MSVC 通常合并,GCC 可能分设) - 虚继承本身不复制基类数据,但
vbptr开销逃不掉 - 普通(非虚)多重继承不会加
vbptr,只叠加各基类对象大小 + 对齐
空类与空白基类优化(EBO)的例外
空类本身 sizeof 是 1,但作为基类时可能被优化掉:
-
struct Empty {};→sizeof(Empty) == 1 -
struct D : Empty { int x; };→sizeof(D)通常是 4(x 占 4),不是 1+4=5,因为编译器启用空白基类优化(EBO) - 但 EBO 不适用于含有虚函数或虚继承的空基类:一旦有
vfptr或vbptr,该基类就不再是“空”的,优化失效 - 静态成员、普通/静态成员函数、友元函数——全部不占对象空间,
sizeof完全无视它们
真正难调试的是对齐和 EBO 的组合效果:成员顺序微调、加一个 virtual、换一个继承方式,都可能让 sizeof 跳变 4 或 8 字节,而且不同编译器结果未必一致。别依赖绝对值,要理解布局逻辑。


















