联合体(Union)是一种构造数据类型。在一个联合体内,我们可以定义多个不同类型的成员,这些成员将会共享同一块内存空间。老版本的 C++ 为了和C语言保持兼容,对联合体的数据成员的类型进行了很大程度的限制,这些限制在今天看来并没有必要,因此 C++11 取消了这些限制。
C++11 标准规定,任何非引用类型都可以成为联合体的数据成员,这种联合体也被称为非受限联合体。C++11 对 C++98 的改进:
允许非 POD 类型POD 是英文 Plain Old Data 的缩写,用来描述一个类型的属性。
POD 类型一般具有以下几种特征(包括 class、union 和 struct等):
- 没有用户自定义的构造函数、析构函数、拷贝构造函数和移动构造函数。
- 不能包含虚函数和虚基类。
- 非静态成员必须声明为 public。
- 类中的第一个非静态成员的类型与其基类不同,
- 在类或者结构体继承时,满足以下两种情况之一:
- 派生类中有非静态成员,且只有一个仅包含静态成员的基类;
- 基类有非静态成员,而派生类没有非静态成员。
- 所有非静态数据成员均和其基类也符合上述规则(递归定义),也就是说 POD 类型不能包含非 POD 类型的数据。
- 此外,所有兼容C语言的数据类型都是 POD 类型(struct、union 等不能违背上述规则)。
允许联合体有静态成员
1
2
3
4
5
6union U {
static int func() {
int n = 3;
return n;
}
};需要注意的是,静态成员变量只能在联合体内定义,却不能在联合体外使用,这使得该规则很没用。
非受限联合体的赋值注意事项
C++11 规定,如果非受限联合体内有一个非 POD 的成员,而该成员拥有自定义的构造函数,那么这个非受限联合体的默认构造函数将被编译器删除;其他的特殊成员函数,例如默认拷贝构造函数、拷贝赋值操作符以及析构函数等,也将被删除。
1 |
|
因为 string 类拥有自定义的构造函数,所以 U 的构造函数被删除;定义 U 的类型变量 u 需要调用默认构造函数,所以 u 也就无法定义成功。
解决上面问题的一般需要用到 placement new(稍后会讲解这个概念)
匿名联合体和枚举式类
匿名联合体是指不具名的联合体(也即没有名字的联合体),一般定义如下:
1 | union U{ |
可以看到,联合体 U 内定义了一个不具名的联合体,该联合体包含一个 int 类型的成员变量,我们称这个联合体为匿名联合体。
同样的,非受限联合体也可以匿名,而当非受限的匿名联合体运用于类的声明时,这样的类被称为“枚举式类”。示例如下:
1 |
|