空基类优化(ebo)是编译器对继承空类时的内存布局优化,子类复用自身起始地址而不额外分配空间;它非标准强制但被主流编译器默认启用,可通过sizeof比较和地址验证是否生效,虚函数、多重相同空基类等会破坏ebo。

什么是空基类优化(EBO)
空基类优化是编译器对继承自空类(即无非静态数据成员、无虚函数、无虚基类的类)时做的内存布局优化:子类不为该空基类单独分配存储空间,而是复用自身对象起始地址。它不是 C++ 标准强制要求,但所有主流编译器(GCC、Clang、MSVC)都默认启用。
关键判断:只要基类是空的且满足标准定义,sizeof 子类通常不会因继承它而增大——但一旦子类有虚函数或基类有虚函数,EBO 就可能失效。
怎么验证 EBO 是否生效
最直接的方式是比对 sizeof:
struct Empty {};
struct Derived : Empty { int x; };
struct WithVtable : Empty { virtual ~WithVtable() = default; };
static_assert(sizeof(Derived) == sizeof(int)); // 通常 true
static_assert(sizeof(WithVtable) > sizeof(int)); // 通常 true(vptr 占位)
- 用
offsetof检查空基类子对象是否真的“零偏移”:offsetof(Derived, Empty)非法,但可以static_cast<empty>(&d)</empty>看地址是否等于&d - 调试时打印
&d和static_cast<empty>(&d)</empty>,地址一致说明 EBO 生效 - 禁用 EBO 测试(如 GCC 的
-fno-empty-bases)可反向验证:此时sizeof(Derived)会变成sizeof(int) + 1(对齐填充)
哪些情况会破坏 EBO
EBO 不是“自动永续”的魔法,几个常见破坏点:
立即学习“C++免费学习笔记(深入)”;
- 基类含虚函数(哪怕一个
virtual destructor)→ 引入 vptr,无法复用地址 - 子类本身有虚函数 → 自身需要 vptr,空基类仍可能被压缩,但若基类也有虚函数,则各自 vptr 无法合并
- 多重继承中两个空基类类型相同(如
struct A : Empty, Empty {})→ C++ 要求它们必须是不同对象,EBO 失效(标准规定) - 使用
[[no_unique_address]]替代继承时,语义不同,不触发 EBO,但能达到类似效果(C++20)
注意:std::tuple、std::function 等标准库实现大量依赖 EBO;自己写 policy-based 类(如 allocator、iterator)时,把策略作为空基类是惯用手法。
手动模拟 EBO 的风险与替代方案
有人试图用 union 或 char[] 手动“挤占”空间来模拟 EBO,这非常危险:
- 违反严格别名规则(strict aliasing),导致未定义行为
- 破坏对象生命周期:空类虽无数据,但有构造/析构语义,跳过它可能引发资源泄漏(比如空类里带
static计数器) - 现代替代更安全:
[[no_unique_address]]可用于成员变量,编译器按需省略其存储(即使类型非空,只要值为常量表达式也可优化)
例如:struct S { [[no_unique_address]] Empty e; int x; }; 在支持 C++20 的编译器上效果接近 EBO,且语义清晰、无继承歧义。
真正要注意的,是别在空基类里加虚函数、别让多个同类型空基类共存、别用 offsetof 去“证明”布局——因为 EBO 是实现细节,标准只保证“可能优化”,不保证“一定发生”。










