# 【C++ 筆記】列舉(Enumeration) - part 34 目錄(Table of Contents): [TOC] --- 很感謝你點進來這篇文章。 你好,我並不是什麼 C++、程式語言的專家,所以本文若有些錯誤麻煩請各位鞭大力一點,我極需各位的指正及指導!!本系列文章的性質主要以詼諧的口吻,一派輕鬆的態度自學程式語言,如果你喜歡,麻煩留言說聲文章讚讚吧! ## Introduction 列舉(Enumeration),簡稱 Enum,是 C++ 中的一種使用者自定義資料型態(User-Defined Type)。可用於定義一組命名的整數常數,這些常數通常用來表示狀態、選項或模式。 * Enum 有助於為整數值賦予有意義的名稱,以提升程式碼的可讀性與可維護性。 * 主要適用於我們對某項有少量可能數值(如方向、星期幾等)時。 使用 Enum 的情境大致上有以下這三種(可能更多,只是舉例而已): * 方向:東、西、南、北 * 遊戲狀態:選單中、遊戲中、暫停、遊戲結束 * 星期幾:週一到週日 ## 定義 enum 與建立一個 enum 在 C++98/03 舊標準中,用關鍵字 `enum` 來定義一個 enum,被稱為「非強型別列舉」或「傳統列舉」。 基本語法: ```cpp= enum EnumName { Value1, Value2, Value3 }; ``` `Value1, Value2, Value3` 等等是常數的名稱,都是唯一的識別字(Identifiers)。預設列舉中第一個名稱會被賦予整數值 0,後續名稱則以 1 遞增。 `Value1 = 0, Value2 = 1, Value3 = 2` 以此類推。 也可以直接手動賦值: ```cpp= enum EnumName { Value1 = v1, Value2 = v2, Value3 = v3 }; ``` v1, v2, v3 的值應為整數。 也不用定義所有常數名稱的值,如果目前常數名稱的值是 x,則後續的值會持續 + 1。 若要建立一個 enum,則要在變數前加上先前定義的 enum 名稱,例如:`EnumName x;`。 也可賦予 enum 內的值作為初始值:`EnumName x = Value1;`。 ### 範例 1:星期幾 ```cpp= #include <iostream> using namespace std; enum Day{ SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, }; int main(){ Day today = WEDNESDAY; cout << "Today's index is " << today << '\n'; } ``` ### 範例 2:東西南北 example from : https://www.geeksforgeeks.org/cpp/enumeration-in-cpp/ ```cpp= #include <iostream> using namespace std; // 定義一個 enum 列舉資料型態 // Defining enum enum direction { EAST, NORTH, WEST, SOUTH }; int main() { // 建立 enum 變數 // Creating enum variable direction dir = NORTH; cout << dir; return 0; } ``` ## 強型別列舉(Enum Classes) C++ 11 引入了這樣的特性,主要彌補先前傳統 `enum` 的兩個缺點: 1. 命名空間汙染(Scope Pollution):傳統列舉的成員名稱是全域的,因此不能在兩個不同的列舉中使用相同的名稱(如 `enum Color { RED };` 和 `enum Alert { RED };` 會衝突)。 2. 隱式轉換(Implicit Conversion):傳統列舉會自動轉換成 `int`,可能導致比較錯誤(如拿「顏色」去跟「星期」做比較,編譯器不會報錯)。 C++ 11 引入 Enum Classes 提供安全性更高的型態,也解決上述這些問題。 接下來看看 Enum Classes 的程式怎麼寫吧! ### 範例 3:以 Enum Classes 撰寫的星期幾程式 example from : https://www.geeksforgeeks.org/cpp/enumeration-in-cpp/ 在以下的範例中,只要在 `enum` 關鍵字後面再加上一個 `class` 即為 Enum Classes 的撰寫方式。 但需要注意有作用域(Scope)的限制,所以要寫成像這樣的形式去存取 `enum class` 的值:`EnumName::Value`。 在印出 enum 變數時不會自動轉換型態,因此要透過 `static_cast` 去做顯式轉換,才能做下一步的印出動作。 ```cpp= #include <iostream> using namespace std; // 定義 enum class // Define the enum class enum class Day { Sunday = 1, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday }; int main() { // 初始化 // initializing Day today = Day::Thursday; // 印出 enum // Print the enum cout << static_cast<int>(today); return 0; } ``` 如果直接印出,寫成像這樣子:`cout << today;` 則會發生以下錯誤: ``` ERROR! /tmp/h7o8IuG5pb/main.cpp: In function 'int main()': /tmp/h7o8IuG5pb/main.cpp:25:10: error: no match for 'operator<<' (operand types are 'std::ostream' {aka 'std::basic_ostream<char>'} and 'Day') 25 | cout << today; | ~~~~ ^~ ~~~~~ | | | | | Day | std::ostream {aka std::basic_ostream<char>} In file included from /usr/local/include/c++/14.2.0/iostream:41, from /tmp/h7o8IuG5pb/main.cpp:1: /usr/local/include/c++/14.2.0/ostream:116:7: note: candidate: 'std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(__ostream_type& (*)(__ostream_type&)) [with _CharT = char; _Traits = std::char_traits<char>; __ostream_type = std::basic_ostream<char>]' 116 | operator<<(__ostream_type& (*__pf)(__ostream_type&)) | ^~~~~~~~ . . . . ``` 然後初始化時也不小心寫成傳統形式 `Day today = Thursday;`,沒有考慮到作用域規則的話,也會產生錯誤: ``` ERROR! /tmp/286k2LGl0z/main.cpp: In function 'int main()': /tmp/286k2LGl0z/main.cpp:21:17: error: 'Thursday' was not declared in this scope; did you mean 'Day::Thursday'? 21 | Day today = Thursday; | ^~~~~~~~ | Day::Thursday /tmp/286k2LGl0z/main.cpp:12:5: note: 'Day::Thursday' declared here 12 | Thursday, | ^~~~~~~~ ``` ### 範例 4:利用運算子重載直接印出 如果覺得顯式轉換太麻煩的話,沒關係!!~~還有更麻煩的寫法~~,讓你直接回到傳統的 enum,一勞永逸的直接把變數 cout 印出來。 ```cpp= #include <iostream> using namespace std; enum class Color { RED, GREEN, BLUE }; ostream& operator<<(std::ostream& os, Color c) { switch (c) { case Color::RED: os << "RED"; break; case Color::GREEN: os << "GREEN"; break; case Color::BLUE: os << "BLUE"; break; default: os << "UNKNOWN"; break; } return os; } int main() { Color myColor = Color::BLUE; cout << "My color is: " << myColor; return 0; } ``` ### 範例 5:遊戲角色狀態 ```cpp= #include <iostream> #include <string> using namespace std; enum class CharacterState { IDLE, // 閒置 RUNNING, // 奔跑 ATTACKING, // 攻擊 DEAD // 死亡 }; void handleState(CharacterState state) { switch (state) { case CharacterState::IDLE: cout << "[角色] 正在待機,觀察四周..." << endl; break; case CharacterState::RUNNING: cout << "[角色] 快速奔跑中!消耗耐力。" << endl; break; case CharacterState::ATTACKING: cout << "[角色] 發動攻擊!造成傷害。" << endl; break; case CharacterState::DEAD: cout << "[角色] 已死亡。請按 R 重新開始。" << endl; break; default: cout << "[系統] 未知狀態錯誤!" << endl; break; } } int main() { CharacterState myHero = CharacterState::IDLE; handleState(myHero); cout << "--- 玩家按下前進鍵 ---" << endl; myHero = CharacterState::RUNNING; handleState(myHero); cout << "--- 玩家遇到敵人 ---" << endl; myHero = CharacterState::ATTACKING; handleState(myHero); cout << "--- 玩家掛機中 ---" << endl; myHero = CharacterState::IDLE; handleState(myHero); cout << "--- 玩家被打死了 ---" << endl; myHero = CharacterState::DEAD; handleState(myHero); return 0; } ``` ## 參考資料 [Enumeration in C++ - GeeksforGeeks](https://www.geeksforgeeks.org/cpp/enumeration-in-cpp/) [C++ 枚举类型详解 | 菜鸟教程](https://www.runoob.com/w3cnote/cpp-enum-intro.html) [C/C++ enum 用法與範例 | ShengYu Talk](https://shengyu7697.github.io/cpp-enum/) [列舉](https://openhome.cc/Gossip/CppGossip/enumType.html)