union


有些類別的實例,可能包含不同型態的成員,然而,在某個時間點上,只會有一個成員是有效的,例如,你可能會設計一個磁頭類別,磁頭讀取磁帶中的資料並儲存為對應的資料型態:

#include <iostream> 
using namespace std; 

class Output {
public:
    virtual void write(char cvalue) = 0;
    virtual void write(int ivalue) = 0;
    virtual void write(double dvalue) = 0;
    virtual ~Output() = default;
};

class Console : public Output {
public:
    void write(char cvalue) override {
        cout << cvalue << endl;
    }
    void write(int ivalue) override {
        cout << ivalue << endl;
    }
    void write(double dvalue) override {
        cout << dvalue << endl;
    }
};

class Head {
    char cvalue;
    int ivalue;
    double dvalue;

    enum {CHAR, INT, DOUBLE} type;

public:
    void read(char cvalue) {
        this->cvalue = cvalue;
        this->type = CHAR;
    }

    void read(int ivalue) {
        this->ivalue = ivalue;
        this->type = INT;
    }

    void read(double dvalue) {
        this->dvalue = dvalue;
        this->type = DOUBLE;
    }

    void writeTo(Output &output) {
        switch(this->type) {
            case CHAR:
                output.write(this->cvalue);
                break;
            case INT:
                output.write(this->ivalue);
                break;
            case DOUBLE:
                output.write(this->dvalue);
                break;
        }
    }
};

int main() { 
    Console console;
    Head head;

    head.read(10);
    head.writeTo(console);

    head.read('A');
    head.writeTo(console);

    return 0;
}

在上例中,Head 一次只儲存一種資料,並依 type 決定該寫出哪種資料,因為 Head 一次只儲存一種資料,不需要分別為 cvalueivaluedvalue 各開一個記憶體空間。

你可以使用 union,它是一種特殊的類別,維護足夠的空間來置放多個資料成員中的一種,而不是為每個資料成員配置各自空間,例如:

...略
class Head {
    union {
        char cvalue;
        int ivalue;
        double dvalue;
    } value;

    enum {CHAR, INT, DOUBLE} type;

public:
    void read(char cvalue) {
        this->value.cvalue = cvalue;
        this->type = CHAR;
    }

    void read(int ivalue) {
        this->value.ivalue = ivalue;
        this->type = INT;
    }

    void read(double dvalue) {
        this->value.dvalue = dvalue;
        this->type = DOUBLE;
    }

    void writeTo(Output &output) {
        switch(this->type) {
            case CHAR:
                output.write(this->value.cvalue);
                break;
            case INT:
                output.write(this->value.ivalue);
                break;
            case DOUBLE:
                output.write(this->value.dvalue);
                break;
        }
    }
};
...略

Head 中定義了匿名的 union 並建立了 value 成員,union 配置足夠大的空間以來容納最大長度的資料成員,以上例而言,最大長度是 double 型態,因此 value 成員的大小是 double 的長度,由於 union 的資料成員共用記憶體空間,存取當前具有合法值的資料成員,才能正確地取資料,

union 是種特殊的類別,因此多數的類別語法也可以用於 union,例如,可以為 union 定義名稱,預設權限為 public,也可以宣告為 protectedprivate,可以定義建構函式、解構函式與成員函式等,然而不能擁有虛擬函式,不能繼承其他類別,也不能作為基礎類別。

在 C++ 11 以後,成員的型態可以是自訂型態,可以有建構函式、解構函式或是複製指定運算子,不過並不建議,因為會令管理成員資源更為複雜。