C++中的联合体(Union)是什么?(在现代C++中还有哪些用法)

1次阅读

union是同一块内存按不同类型解读的裸金属操作,不管理生命周期、不调用构造/析构函数,仅支持平凡可复制类型,现代c++中主要用作零开销类型双关或底层内存结构封装

C++中的联合体(Union)是什么?(在现代C++中还有哪些用法)

Union 是什么:内存复用的裸金属操作

Union 不是“多个类型可选”,而是“同一块内存按不同方式解读”。它不管理生命周期,不调用构造/析构函数,C++11 之前连 std::String 这类类型都不能放进去——不是语法报错,而是行为未定义。

  • 所有成员共享起始地址,sizeof(union) 等于最大成员对齐后大小
  • 写入 member_a 后读 member_b,结果取决于位模式和平台 ABI(比如 Float 写入、int 读出,得到的是 IEEE754 位解释)
  • 没有隐式类型安全:编译器不会阻止你读刚写入的另一个成员,但 C++17 起若类型不满足“可平凡复制”或“布局兼容”,就是未定义行为

现代 C++ 中还能用 Union 做什么

真正还在用的场景非常窄,基本只剩两类:需要零开销的类型双关(type punning),或封装手动内存管理的底层结构(如自定义 variant、序列化缓冲区头)。

  • std::bit_cast(C++20)已替代大部分 union 类型双关需求,更安全、更明确,且不依赖 union 的“活跃成员”规则
  • 嵌入式或协议解析中,仍有人用 union 对齐固定字节布局,例如:
    union Header { uint32_t raw; struct { uint8_t a, b, c, d; }; };

    但要注意:这种用法依赖 #pragma packalignas 控制填充,否则结构体内存布局不可控

  • 手写 variant 实现时,union 是存储区基础,但必须配合 tag 字段 + 手动调用 std::construct_at/std::destroy_at 管理对象生命周期

为什么别直接在 Union 里放 std::string 或 vector

因为它们有非平凡构造函数、析构函数和拷贝语义。Union 不会自动调用这些函数,一旦你写入一个 std::string 又覆盖写入 int,前者资源(内存)就泄漏了;若之后再读 string,会尝试析构一个未构造的对象,触发崩溃。

  • C++11 起,只有“平凡可复制”(trivially copyable)类型才能无条件放进 union;std::string 不满足该要求
  • 即使你用 placement new 手动构造,也必须严格保证:每次只激活一个成员,且在切换前显式析构旧成员
  • 错误示例:
    union U { std::string s; int i; }; U u; u.s = "hello"; u.i = 42; // ❌ s 的析构没被调用,内存泄漏

替代方案比硬啃 Union 更靠谱

95% 的原意想用 union 的场合,其实要的是“运行时单值多类型”——那直接用 std::variant;想绕过类型系统做位操作,优先用 std::bit_castmemcpy;真要极致控制内存,也建议封装成类,把 union 包在里面并严格约束接口

立即学习C++免费学习笔记(深入)”;

  • std::variant 自动管理生命周期、支持访问者模式、能静态检查是否覆盖所有 case
  • reinterpret_cast + char* 指针做类型双关?不行,违反 strict aliasing 规则;memcpy 到临时 buffer 是合法 fallback
  • union 本身不能被继承、不能有虚函数、不能作为基类——它就是一个纯数据容器,连 = default 的拷贝赋值都要你自己写

真正难的从来不是 union 怎么写,而是判断“此刻是否真的需要绕过类型系统”。多数时候,答案是否定的。

text=ZqhQzanResources