列舉二:有範圍的列舉 Enum Class
列舉有兩種,一種是無範圍的 unscoped enum,第二種則是有範圍的 scoped enum。
上一章 列舉一:無範圍的列舉 Enum 我們講解了無範圍的列舉,並提到它的功能、局限性、以及缺點。
今天這章就是來介紹什麼是有範圍的 scoped enum,並且了解它如何處理了我們在無範圍的列舉所遇到的問題。
有範圍 scoped enum
首先,讓我們先來理解 scoped enum 和 unscoped enum 的差別。
其實,兩者的功能幾乎是一樣的。唯獨有兩個最大的差別:
- 型態安全
- 獨立名稱空間
第一點基本上就解決了前一章最後我們遇到型態轉換的問題。
第二點則解決了編譯器無法識別擁有同一個名稱的符號的問題。
讓我們拿上一章最後一個的程式碼,並將關鍵字 enum
更改為 enum class
。
enum class Animal { bird, cat }; enum class Color { red, green }; int main() { Animal animal { Animal::bird }; // 需要使用::來讀取列舉值 Color color { Color::red }; // 需要使用::來讀取列舉值 if (animal == color) // 編譯錯誤!因為編譯器知道兩個是不同型別,所以無法比較 std::cout << "color and animal are equal\n"; else std::cout << "color and animal are not equal\n"; return 0; }
運行如上程式碼後,我們會發現,編譯器這次就知道要報錯了!
原因就是因為 enum class 是型態安全的,編譯器並不會隨意將列舉值轉為整數型態,而是使用原本的型態。
在這個例子中,原本的型態分別為 Animal
和 Color
,編譯器不知道該如何比較兩個不同型態的東西,因此只能報錯。
另外值得注意的一點,當我們使用 enum class 時,我們必須使用 ::
運算子加上其名稱空間才能讀取列舉值。這和 名稱空間 namespace 的使用方法相同。
這是因為上面提到的第二點差異:獨立的命名空間。現在因為每一個列舉都是獨立的名稱空間,所以我們必須藉由特別標示其所在的名稱空間才能使用。
常見應用
講了這麼多,所以列舉到底有什麼用呢?
列舉是程式中非常有用的工具,因為它可以幫助我們提升程式碼的可讀性使其更容易理解。當我們需要為一小組相關的事物命名時,例如星期幾或指南針方向,我們就可以使用列舉!
因為我們不需要記住每個事物的各種不同名稱,而是只需使用一個可以代表它們所有的列舉就可以了。
讓我們來看看下面的例子:
enum class Directions { up, down, right, left }; enum class FamilyMembers { dad, mom, sister, brother }; enum class DaysOfWeek { monday, tuesday, wednesday, thursday, friday, saturday, sunday };
或著另一個實用的例子,我們可以建立一個可以提供讀檔是否成功的列舉,大家可以體會看看這樣程式碼是不是更易讀了一些!比如說:
enum class Result { Success, ErrorOpen, ErrorRead }; int main() { FileReadResult result {Result::Success}; if (!openFile()) result = Result::ErrorOpen; if (!readFile()) result = Result::ErrorRead; return 0; }
總結
好啦講到這裡就差不多講完列舉了!花了兩個篇章講了什麼是列舉。
其實重點很簡單,就是 enum class 相較於一般 enum 在於他有更高的安全性。因為編譯器並不會隨意將列舉值轉換為整數,以及每個列舉都存在於獨立的命名空間!
想當初我在面試工作的時候被問到這時雖然知道 enum class 比較安全,但卻不知道為什麼。
希望大家看完這兩篇後能夠清楚了解 enum 與 enum class!