Cplusplus的共用体
cplusplus 共用体(union)
概述
- 共用体(union)是一种特殊的数据类型, 它允许在相同的内存位置存储不同的数据类型, 但任何时候只能有一个成员“活跃”. 这种设计使得共用体成为一种节省内存的工具,特别适用于需要以多种格式处理同一数据块的低级编程、协议解析或资源受限的嵌入式系统.
定义
#include <stdio.h>
union Data {
int i;
float f;
char str[20];
};
int main() {
union Data data;
data.i = 10;
printf("data.i = %d\n", data.i);
data.f = 3.14;
printf("data.f = %.2f\n", data.f);
// 注意:此时 data.i 的值已经被覆盖
printf("data.i (after writing f) = %d\n", data.i);
return 0;
}
/**
Output:
data.i = 10
data.f = 3.14
data.i (after writing f) = 1078523331
*/
使用 union 的缺陷
-
类型安全缺失
点击展开#include <iostream> using namespace std; union U { int i; float f; }; int main() { U u; u.i = 0x3f800000; // 浮点数 1.0f 的二进制编码 std::cout << u.f << std::endl; } /** Output: 1.0 explain: 1. u.i = 0x3f800000 把这段内存写成了 0x3f800000 2. u.f 读取时,把这段内存当成 float. 3. float 解释 0x3f800000 → 1.0. */ -
构造函数 / 析构函数管理困难
错误的版本
正确的版本
// 错误的版本
#include <iostream>
#include <string>
union U {
std::string s; // 非平凡类型
int i;
// 构造函数
U() {
// placement new(定位 new)语法
// 在地址 &s 指向的内存上构造一个对象,但不分配新内存.
// 它不会调用 operator new,不会分配堆内存,只是把对象构造在指定位置(&s).
new (&s) std::string("hello");
std::cout << "Construct string\n";
}
~U() { // 析构函数
s.~basic_string(); // 手动析构 string
std::cout << "Destruct string\n";
}
};
int main() {
U u; // 调用构造函数,构造 string
std::cout << u.s << std::endl;
// 切换激活成员
u.s.~basic_string(); // 必须手动析构 string
u.i = 42; // 激活 int 成员
std::cout << u.i << std::endl;
// 程序结束时,U 的析构函数会再次析构 string(但 string 已经被手动析构过一次)
// → 这是未定义行为(double free)
}
// 正确的版本
#include <iostream>
#include <string>
struct U {
union Storage {
std::string s;
int i;
Storage() {} // 不自动构造任何成员
~Storage() {} // 不自动析构任何成员
} data;
bool active_string;
U() : active_string(true) {
new (&data.s) std::string("hello");
}
void setInt(int v) {
if (active_string) {
data.s.~basic_string();
active_string = false;
}
data.i = v;
}
void setString(const std::string& str) {
if (!active_string) {
new (&data.s) std::string(str);
active_string = true;
} else {
data.s = str;
}
}
~U() {
if (active_string) {
data.s.~basic_string();
}
}
};
int main() {
U u;
std::cout << u.data.s << std::endl;
u.setInt(42);
std::cout << u.data.i << std::endl;
u.setString("world");
std::cout << u.data.s << std::endl;
return 0;
}
使用 std::variant 替代 union
- std::variant 是 C++17 引入的一种 类型安全的联合体(type‑safe union). 它的核心作用是:在同一块存储中保存多个可能类型之一,但自动管理构造/析构、记录当前激活类型,并保证类型安全.
- std::variant<T1, T2, …> 表示一个对象,它在任意时刻只能保存其中一种类型的值.
定义
#include <iostream>
#include <string>
#include <variant>
// helper for std::visit (C++17)
/**
template <class... Ts>: C++11引入的变长参数模板(Variadic Templates),
struct overloaded : Ts...: 多重继承,继承所有 Ts.
*/
template <class... Ts>
struct overloaded : Ts... { using Ts::operator()...; };
template <class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
int main() {
using Value = std::variant<int, double, std::string>;
// 构造
Value v = 42;
std::cout << "v holds int? " << std::holds_alternative<int>(v) << "\n";
v = 3.14;
std::cout << "v holds double? " << std::holds_alternative<double>(v) << "\n";
v = std::string("hello variant");
// 访问:std::get
try {
std::string s = std::get<std::string>(v);
std::cout << "std::get<string>: " << s << "\n";
} catch (const std::bad_variant_access& e) {
std::cout << "bad_variant_access: " << e.what() << "\n";
}
// 访问:std::get_if
if (auto p = std::get_if<double>(&v)) {
std::cout << "get_if<double>: " << *p << "\n";
} else {
std::cout << "v is not double\n";
}
// 访问:std::visit
std::visit(overloaded{
[](int x) { std::cout << "visit int: " << x << "\n"; },
[](double d) { std::cout << "visit double: " << d << "\n"; },
[](const std::string& s) { std::cout << "visit string: " << s << "\n"; }
}, v);
// emplace
v.emplace<int>(123);
std::cout << "after emplace<int>: " << std::get<int>(v) << "\n";
return 0;
}