C++ enum列舉型別詳解

2020-07-16 10:04:36
《enum列舉用法攻略》一節已經介紹過列舉資料型別,還記得嗎?它們是由程式設計師定義的資料型別,由一組稱為列舉量的值組成,列舉量代表整數常數。本節將進一步介紹列舉資料型別的應用,以及使用它們能做和不能做的事情。

在同一個語句中宣告 enum 資料型別並定義變數

以下程式碼使用了兩行來宣告一個列舉資料型別,並定義了該型別的變數:

enum Car {PORSCHE, FERRARI, JAGUAR};
Car sportsCar;

但是,C++ 允許在同一個語句中宣告一個列舉資料型別,並定義該型別的一個或多個變數。因此,上面的程式碼可以改寫為如下形式:

enum Car {PORSCHE, FERRARI, JAGUAR} sportsCar;

以下語句不但宣告了 Car 資料型別,而且定義了 2 個變數 myCar 和 yourCar:

enum Car {PORSCHE, FERRARI, JAGUAR} myCar, yourCar;

將整數賦值給 enum 變數

雖然列舉資料型別的列舉量在記憶體中是以整數形式儲存的,但是,並不能直接將整數值賦給 enum 變數。例如,假設程式中包含以下宣告:

enum Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY };
Day today;

現在可以編寫以下賦值語句:

today = THURSDAY;

但是,正如前面已經介紹過的,以下語句是非法的。如果試圖編譯它,則會出現一個錯誤訊息。

today = 3; //錯誤

在給 enum 變數賦值時,應該使用有效的列舉量。但是,如果某些情形要求必須將整數值儲存在 enum 變數中,則可以通過強制轉換的方式,將整數轉換為 enum 資料型別,範例如下:

today = static_cast<Day>(3);

該語句的作用與以下語句是一樣的:

today = THURSDAY;

將列舉量賦值給 int 變數

雖然不能直接將整數賦值給 enum 變數,但是,反過來卻是可以的,即可以將列舉量賦值給整型變數。例如,以下程式碼將可以正常執行:
enum Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY };
int today = THURSDAY;
Day workday = FRIDAY;
int tomorrow = workday;
cout << today <<" "<< tomorrow << endl;
當該程式碼執行時,它將顯示 3 4

使用數學運算子改變 enum 變數的值

雖然列舉量實際上就是整數,而且 enum 變數真正儲存的也是整數值,但是,如果試圖使用它們執行數學運算,那麼還是會遇到問題。

例如,來看以下程式碼:

Day day1, day2; // 定義 2 個 Day 變數
day1 = TUESDAY; // 將 TUESDAY 賦值給 day1
day2 = day1 + 1; //錯誤!該語句不起作用

第 3 個語句之所以會出現問題,是因為表示式 day1+1 的結果為整數值 2,然後賦值運算子會試圖將整數值 2 賦給 enum 變數 day2,但是前面已經介紹過,C++ 並不會隱式地將 int 轉換為 Day 型別,所以出現了問題。

要解決該問題,可以如前文所述,通過強制轉換的方式,將整數值結果轉換為 Day 資料型別,範例如下:

day2 = static_cast<Day> (day1 + 1); //該語句有效

使用列舉量輸出值

正如前文所述,將列舉量傳送到 cout 將顯示列舉量的整數值。例如,假設使用前面定義的 Day 型別,以下語句將顯示 0:

cout << MONDAY << endl;

如果要讓列舉量顯示諸如“Monday”這樣的字串,則需要編寫程式碼來產生所需的字 符串。例如,如果 workDay 是一個已經被初始化為某個值的 Day 變數,則以下switch語句將根據變數的值顯示對應的日期名稱:
switch(workDay)
{
    case MONDAY :
        cout <<"Monday";
        break;
    case TUESDAY :
        cout <<"Tuesday";
        break;
    case WEDNESDAY:
        cout <<"Wednesday";
        break;
    case THURSDAY :
        cout <<"Thursday";
        break;
    case FRIDAY :
        cout <<"Friday";
        break;
}

使用列舉量控制迴圈

因為列舉量是作為整數儲存在記憶體中的,所以可以使用它們控制迴圈疊代的次數。但是,如前文所述,不能直接將數學運算的結果賦值給列舉量,而必須先將結果強制轉換為 enum 資料型別。所以,不能直接對 enum 變數使用 ++ 或 -- 運算子。以下語句將無法執行。
Double sale, total = 0.0;
for ( Day workday = MONDAY; workday <= FRIDAY ; workday++) // 錯誤
{
    cout << "Enter the sales for day " << (workday +1) << ": ";
    cin >> sales;
    total += sales;
}
要解決該問題,可以將以上語句修改為如下形式:

for ( Day workday = MONDAY; workday <= FRIDAY; workday = static_cast<Day>(workday + 1))

但是,更簡單的方法是將迴圈控制變數變成 int 型別。按照這個思路,可以將上面的語句修改如下:
for ( int workday = MONDAY; workday <= FRIDAY;workday++)
{
    cout << "Enter the sales for day " << (workday + 1) <<":";
    cin >> sales;
    total += sales;
}

使用C++ 11中的強型別 enum

C++ 不允許相同作用域內的多個列舉量具有相同的名稱,也就是說,在同一作用域內,即使是兩個不同的列舉資料型別,也不能定義或使用相同的列舉量名稱作為自己的成員。但是,C++11 包含了一個新型別:enum,即所謂的強型別列舉,也稱為 enum 類,可以擺脫這種限制。

以下是強型別 enum 宣告的 2 個範例:

enum class Presidents { MCKINLEY, ROOSEVELT, TAFT };
enum class VicePresidents { ROOSEVELT, FAIRBANKS, SHERMAN };

以上語句定義了 2 個強型別 enum:Presidents 和 VicePresidents。注意,它們看起來和常規 enum 宣告是一樣的,區別在於,在 enum 後面多了一個單詞 class。雖然這 2 個 enum 包含了相同的列舉量 ROOSEVELT,但是它們在編譯時已經不會出錯了。

但是,在使用強型別列舉資料型別時,必須給每個列舉量新增它所屬 enum 名作為字首,後接 :: 操作符。以下是 3 個範例:
Presidents prez = Presidents::ROOSEVELT;
VicePresidents vp1 = VicePresidents::ROOSEVELT;
VicePresidents vp2 = VicePresidents::SHERMAN;
第一個語句定義了名為 prez 的 Presidents 變數,並且使用了 Presidents::ROOSEVELT 列舉量作為它的初始化值;第二個語句定義了名為 vpl 的 VicePresidents 變數,並且使用了 VicePresidents::ROOSEVELT 列舉量作為它的初始化值;第三個語句定義了名為 vp2 的 VicePresidents 變數,並且使用了 VicePresidents::SHERMAN 列舉量作為它的初始化值。

請注意,即使列舉量 SHERMAN 是列舉資料型別中唯一的成員,也必須加上它所屬的 enum 名稱作為字首。

以下是使用列舉量比較 prez 變數的if語句範例:
if (prez == Presidents::ROOSEVELT)
    cout <<"Roosevelt is president! n";
強型別列舉量和常規列舉量一樣,也是作為整數儲存的,但是,如果要檢索一個強型別列舉量底層的整數值,則必須使用強制轉換運算子。以下範例可以將 Presidents::ROOSEVELT 列舉量底層的整數值賦值給變數 x:

int x = static_cast<int>(Presidents::ROOSEVELT);

以下是另外一個範例,它顯示了 Presidents::ROOSEVELT 和 Presidents::TAFT 列舉量的整數值:

cout<<static_cast<int>(Presidents :: ROOSEVELT) << " " <<static_cast<int>(Presidents::TAFT) << endl;

在宣告強型別 enum 時,可以選擇性地指定任意整數資料型別作為底層型別。只要在 enum 名稱後面加上一個冒號(:),然後跟上所需的資料型別即可。例如,以下語句宣告了一個使用 char 資料型別作為其列舉量的 enum:

enum class Day : char { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY };

以下語句顯示了另外一個範例。該語句宣告了一個名為 Water 的 enum,使用了 unsigned 作為其列舉量的資料型別。此外,還給列舉量賦值了。

enum class Water : unsigned { FREEZING = 32, BOILING = 212 };

下面的程式演示了強型別列舉資料型別的用法:
#include <iostream>
using namespace std;

enum class Presidents { MCKINLEY, ROOSEVELT, TAFT };
enum class VicePresidents { ROOSEVELT, FAIRBANKS, SHERMAN };

int main()
{
    Presidents prez = Presidents::ROOSEVELT;
    VicePresidents vp1 = VicePresidents::ROOSEVELT;
    VicePresidents vp2 = VicePresidents::SHERMAN;
    cout << static_cast<int>(prez) << " " << static_cast<int>(vp1) << " " << static_cast<int> (vp2) << endl;
    return 0;
}
程式輸出結果:

10 2