[C++ primer]联合:节省空间的类

联合是一种特殊的类。一个 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节

posted on 2015-07-19 19:00  kona  阅读(265)  评论(0编辑  收藏  举报