联合是一种特殊的类。一个 union 对象可以有多个数据成员,但在任何时刻,只有一个成员可以有值。当将一个值赋给 union 对象的一个成员的时候,其他所有都变为未定义的。
为 union 对象分配的存储的量至少与包含其最大数据成员的一样多。像任何类一样,一个 union 定义了一个新的类型。
1、定义联合
联合提供了便利的办法表示一组相互排斥的值,这些值可以是不同类型的。
// objects of type TokenValue have a single member, // which could be of any of the listed types union TokenValue { char cval; int ival; double dval; };//sizeof(TokenValue)=sizeof(double)
每个 union 对象的大小在编译时固定的:它至少与 union 的最大数据成员一样大。
2、没有静态数据成员、引用成员或类数据成员
(联合里面的东西共享内存,所以静态、引用都不能用,因为他们不可能共享内存。)
某些(但不是全部)类特征同样适用于 union。例如,像任何类一样,union可以指定保护标记使成员成为公用的、私有的或受保护的。默认情况下,union 表现得像 struct:除非另外指定,否则 union 的成员都为 public 成员。
union 也可以定义成员函数,包括构造函数和析构函数。但是,union 不能作为基类使用,所以成员函数不能为虚数。
union 不能具有静态数据成员或引用成员,而且,union 不能具有定义了构造函数、析构函数或赋值操作符的类类型的成员:
union illegal_members { Screen s; // error: has constructor static int is; // error: static member int &rfi; // error: reference member Screen *ps; // ok: ordinary built-in pointer type };
这个限制包括了具有带构造函数、析构函数或赋值操作符的成员的类。
3、使用联合类型
union 的名字是一个类型名:
TokenValue first_token = {‘a‘}; // initialized TokenValue TokenValue last_token; // uninitialized TokenValue object TokenValue *pt = new TokenValue; // pointer to a TokenValue object
像其他内置类型一样,默认情况下 union 对象是未初始化的。可以用与显式初始化简单类对象一样的方法显式初始化 union 对象。但是,只能为第一个成员提供初始化式。该初始化式必须括在一对花括号中。first_token 的初始化给它的 cval 成员一个值。
4、使用联合的成员
可以使用普通成员访问操作符(. 和 ->)访问 union 类型对象的成员:
last_token.cval = ‘z‘;
pt->ival = 42;
给 union 对象的某个数据成员一个值使得其他数据成员变为未定义的。使用 union 对象时,我们必须总是知道 union 对象中当前存储的是什么类型的值。通过错误的数据成员检索保存在 union 对象中的值,可能会导致程序崩溃或者其他不正确的程序行为。
注:避免通过错误成员访问 union 值的最佳办法是,定义一个单独的对象跟踪 union 中存储了什么值。这个附加对象称为 union 的判别式。
5、嵌套联合
union 最经常用作嵌套类型,其中判别式是外围类的一个成员:
class Token { public: // indicates which kind of value is in val enum TokenKind {INT, CHAR, DBL}; TokenKind tok; union { // unnamed union char cval; int ival; double dval; } val; // member val is a union of the 3 listed types };
这个类中,用枚举对象 tok 指出 val 成员中存储了哪种值,val 成员是一个(未命名的)union,它保存 char、int 或 double 值。
经常使用 switch 语句测试判别式,然后根据 union 中当前存储的值进行处理:
Token token; switch (token.tok) { case Token::INT: token.val.ival = 42; break; case Token::CHAR: token.val.cval = ‘a‘; break; case Token::DBL: token.val.dval = 3.14; break; }
6、匿名联合
不用于定义对象的未命名 union 称为匿名联合。匿名 union 的成员的名字出现在外围作用域中。例如,使用匿名 union 重写的 Token 类如下:
class Token { public: // indicates which kind of token value is in val enum TokenKind {INT, CHAR, DBL}; TokenKind tok; union { // anonymous union char cval; int ival; double dval; }; };
因为匿名 union 不提供访问其成员的途径,所以将成员作为定义匿名union 的作用域的一部分直接访问。重写前面的 switch 以便使用类的匿名union 版本,如下:
Token token; switch (token.tok) { case Token::INT: token.ival = 42; break; case Token::CHAR: token.cval = ‘a‘; break; case Token::DBL: token.dval = 3.14; break; }
注:匿名 union 不能有私有成员或受保护成员,也不能定义成员函数。
原文:C++primer(第四版)18.5节