C++中的联合体(union)是一种能让多个不同数据成员共享同一块内存空间的特殊数据类型,它和结构体类似但内存使用方式差异很大。联合体在需要节省内存或者处理同一数据不同表示的场景下非常实用,而和类结合使用还能扩展其功能边界。

C++中联合体的基础定义方式
联合体的定义使用union关键字,基础语法和结构体类似,所有成员共享起始地址相同的内存空间,联合体的大小等于其最大成员的大小。
下面是一个简单的联合体定义示例:
#include <iostream>
using namespace std;
// 定义基础联合体
union Data {
int int_val;
float float_val;
char char_val;
};
int main() {
Data d;
// 给int成员赋值
d.int_val = 10;
cout << "int_val: " << d.int_val << endl;
// 此时float_val的值是未定义的,因为内存被int_val占用
cout << "float_val: " << d.float_val << endl;
// 给float成员赋值,会覆盖之前int_val的内存
d.float_val = 3.14f;
cout << "float_val: " << d.float_val << endl;
cout << "int_val: " << d.int_val << endl;
return 0;
}
上述代码中,Data联合体的三个成员共享同一块内存,修改其中一个成员的值会影响其他成员的解读结果。
union和类混合用法的常见形式
C++中的union可以包含构造函数、析构函数、成员函数,也可以有访问权限控制,这些特性让它能和类的特性结合使用,常见的混合用法有以下几种。
带访问控制的联合体
联合体可以像类一样使用public、private、protected访问修饰符,限制成员的访问范围。
#include <iostream>
using namespace std;
union PrivateUnion {
private:
int secret_int;
float secret_float;
public:
void set_int(int val) {
secret_int = val;
}
int get_int() {
return secret_int;
}
void set_float(float val) {
secret_float = val;
}
float get_float() {
return secret_float;
}
};
int main() {
PrivateUnion pu;
pu.set_int(20);
cout << "get_int: " << pu.get_int() << endl;
pu.set_float(2.718f);
cout << "get_float: " << pu.get_float() << endl;
// 直接访问secret_int会编译报错,因为是私有成员
// pu.secret_int = 10;
return 0;
}
联合体作为类的成员
可以将联合体作为类的内部成员,用来封装需要共享内存的数据,类对外提供统一的访问接口。
#include <iostream>
#include <string>
using namespace std;
class DataWrapper {
private:
// 内部联合体定义
union InnerUnion {
int id;
float score;
};
InnerUnion data;
bool is_int_type; // 标记当前存储的数据类型
public:
DataWrapper(int val) {
data.id = val;
is_int_type = true;
}
DataWrapper(float val) {
data.score = val;
is_int_type = false;
}
void print_data() {
if (is_int_type) {
cout << "存储的是int类型,值: " << data.id << endl;
} else {
cout << "存储的是float类型,值: " << data.score << endl;
}
}
};
int main() {
DataWrapper dw1(100);
dw1.print_data();
DataWrapper dw2(95.5f);
dw2.print_data();
return 0;
}
包含类类型成员的联合体
C++11之后,联合体可以包含具有非平凡构造函数和析构函数的类类型成员,不过需要手动管理这些成员的构造和析构。
#include <iostream>
#include <string>
using namespace std;
union ClassMemberUnion {
int num;
string str; // string是类类型,有非平凡构造和析构
// 需要显式定义构造函数和析构函数
ClassMemberUnion() : num(0) {}
~ClassMemberUnion() {
// 这里需要根据实际存储的类型调用对应成员的析构
// 实际使用中需要额外标记当前激活的成员
}
void set_num(int val) {
num = val;
}
void set_str(const string& val) {
new (&str) string(val); // 手动构造string成员
}
void clear_str() {
str.~string(); // 手动析构string成员
}
};
int main() {
ClassMemberUnion cmu;
cmu.set_num(50);
cout << "num: " << cmu.num << endl;
cmu.set_str("hello");
cout << "str: " << cmu.str << endl;
cmu.clear_str(); // 使用完string成员后手动析构
return 0;
}
union和类混合使用的注意事项
- 联合体的所有成员共享内存,同一时间只能有一个成员是有效的,需要额外维护类型标记来明确当前存储的数据类型。
- 如果联合体包含有非平凡构造/析构的类类型成员,必须手动管理这些成员的生命周期,编译器不会自动调用对应的构造和析构函数。
- 联合体的大小是其最大成员的大小,即使其他成员很小,也会占用对应大小的内存,需要结合场景评估内存收益。
- 匿名联合体和类结合使用时,可以直接访问其成员,不需要通过联合体变量名,适合简单的共享内存场景。
适用场景总结
union和类混合的用法适合以下场景:需要节省内存空间,同一变量可能有多种类型表示,需要封装共享内存的逻辑对外提供统一接口。比如协议解析中同一个字段可能有不同的数据类型,或者嵌入式开发中需要优化内存占用的场景。合理使用这种用法可以在保证功能的同时提升内存使用效率。