# 【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)