联合体(Union)是一种构造数据类型。在一个联合体内,我们可以定义多个不同类型的成员,这些成员将会共享同一块内存空间。老版本的 C++ 为了和C语言保持兼容,对联合体的数据成员的类型进行了很大程度的限制,这些限制在今天看来并没有必要,因此 C++11 取消了这些限制。

C++11 标准规定,任何非引用类型都可以成为联合体的数据成员,这种联合体也被称为非受限联合体。C++11 对 C++98 的改进:

  1. 允许非 POD 类型POD 是英文 Plain Old Data 的缩写,用来描述一个类型的属性。

    POD 类型一般具有以下几种特征(包括 class、union 和 struct等):

    1. 没有用户自定义的构造函数、析构函数、拷贝构造函数和移动构造函数。
    2. 不能包含虚函数和虚基类。
    3. 非静态成员必须声明为 public。
    4. 类中的第一个非静态成员的类型与其基类不同,
    5. 在类或者结构体继承时,满足以下两种情况之一:
      • 派生类中有非静态成员,且只有一个仅包含静态成员的基类;
      • 基类有非静态成员,而派生类没有非静态成员。
    6. 所有非静态数据成员均和其基类也符合上述规则(递归定义),也就是说 POD 类型不能包含非 POD 类型的数据。
    7. 此外,所有兼容C语言的数据类型都是 POD 类型(struct、union 等不能违背上述规则)。
  2. 允许联合体有静态成员

    1
    2
    3
    4
    5
    6
    union U {
    static int func() {
    int n = 3;
    return n;
    }
    };

    需要注意的是,静态成员变量只能在联合体内定义,却不能在联合体外使用,这使得该规则很没用。

非受限联合体的赋值注意事项

C++11 规定,如果非受限联合体内有一个非 POD 的成员,而该成员拥有自定义的构造函数,那么这个非受限联合体的默认构造函数将被编译器删除;其他的特殊成员函数,例如默认拷贝构造函数拷贝赋值操作符以及析构函数等,也将被删除。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <string>
using namespace std;
union U {
string s;
int n;
public:
// placement new
// 如果不加这个,则会构造失败,因为 U 的构造函数被删除
U() { new(&s) string; }
~U() { s.~string(); }
};
int main() {
U u;
return 0;
}

因为 string 类拥有自定义的构造函数,所以 U 的构造函数被删除;定义 U 的类型变量 u 需要调用默认构造函数,所以 u 也就无法定义成功。

解决上面问题的一般需要用到 placement new(稍后会讲解这个概念)

匿名联合体和枚举式类

匿名联合体是指不具名的联合体(也即没有名字的联合体),一般定义如下:

1
2
3
union U{
union { int x; }; //此联合体为匿名联合体
};

可以看到,联合体 U 内定义了一个不具名的联合体,该联合体包含一个 int 类型的成员变量,我们称这个联合体为匿名联合体。

同样的,非受限联合体也可以匿名,而当非受限的匿名联合体运用于类的声明时,这样的类被称为“枚举式类”。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <cstring>
using namespace std;

class Student{
public:
Student(bool g, int a): gender(g), age(a){}
bool gender;
int age;
};

class Singer {
public:
enum Type { STUDENT, NATIVE, FOREIGENR };
Singer(bool g, int a) : s(g, a) { t = STUDENT; }
Singer(int i) : id(i) { t = NATIVE; }
Singer(const char* n, int s) {
int size = (s > 9) ? 9 : s;
memcpy(name , n, size);
name[size] = '\0';
t = FOREIGENR;
}
~Singer(){}
private:
Type t;
union {
Student s;
int id;
char name[10];
};
};

int main() {
Singer(true, 13);
Singer(310217);
Singer("J Michael", 9);
return 0;
}

转自:http://c.biancheng.net/view/7165.html