union仅占用最大成员的内存,如union{int a;char b;double c;}占8字节而非16字节以上,但需手动管理生命周期、对齐及活跃成员,否则引发未定义行为或崩溃。

union 在 C++ 中到底省多少内存
它只占用所有成员中最大那个的大小,不会累加。比如 union { int a; char b; double c; } 占用空间等于 sizeof(double)(通常是 8 字节),而不是 sizeof(int)+sizeof(char)+sizeof(double)(通常 16 字节以上)。
但这个“省”是有代价的:同一时刻只能安全使用一个成员,写入一个成员后读另一个是未定义行为(除非是公共初始子序列或满足标准规定的特殊情况)。
什么时候用 union 真正安全且有用
典型场景是实现类型擦除、状态机、或手动管理内存布局(如网络协议解析、硬件寄存器映射)。C++17 引入了 std::variant,它更安全,但有运行时开销;而裸 union 零成本,前提是程序员自己保证访问逻辑正确。
- 需要精确控制内存布局(如嵌入式、驱动开发)
- 性能敏感,不能接受
std::variant的 tag 存储和访问检查开销 - 配合
enum class手动管理当前活跃成员(即所谓“discriminated union”模式)
必须手动管理构造/析构,否则会崩溃
普通 union 不会自动调用成员的构造函数或析构函数。如果成员含非平凡构造函数(如 std::string、std::vector),直接赋值会导致对象未构造就访问,或对象已析构还去读 —— 典型表现是 Segmentation fault 或随机内存错误。
正确做法是用 union + placement new + 显式析构:
union Data {
int i;
double d;
std::string s; // 非平凡类型
<pre class="brush:php;toolbar:false;">Data() : i(0) {} // 必须提供默认构造,初始化某个平凡成员
~Data() {} // 不能在这里析构 s!需外部控制
void set_string(const char* str) {
s.~basic_string(); // 先显式析构旧对象(如果之前构造过)
new(&s) std::string(str); // placement new 构造新对象
}};
漏掉析构或重复构造,就会泄漏或 double-free。
别忘了对齐和 padding 的干扰
union 的对齐要求是其所有成员对齐要求的最大值。例如含 long double(可能对齐到 16 字节)和 char 的 union,整个 union 会按 16 字节对齐 —— 这可能让数组中每个元素都浪费空间,反而抵消节省。
检查实际布局用 static_assert 和 offsetof:
static_assert(sizeof(Data) == 24, "unexpected size"); static_assert(offsetof(Data, s) == 0, "s must be at offset 0");
结构体嵌套 union 时,padding 更难预测;建议用 #pragma pack 谨慎控制(仅限必要场景,跨平台时尤其危险)。
真正省内存的关键不在 union 本身,而在你是否能严格约束访问路径、避免冗余对象、并接受手动生命周期管理带来的复杂度。稍一松懈,bug 就藏在内存布局的缝隙里。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











