%% # 纲要 > 主干纲要、Hint/线索/路标 # Q&A #### 已明确 ![[02-开发笔记/01-cpp/类型相关/cpp-枚举类型 Enumeration#^rin6gm]] #### 待明确 > 当下仍存有的疑惑 **❓<font color="#c0504d"> 有什么问题?</font>** # Buffer ## 闪念 > sudden idea ## 候选资料 > Read it later ❓<font color="#c0504d">枚举与枚举类有什么区别?</font> ❓<font color="#c0504d"> 枚举值是如何使用的?</font> %% <br><br> # 枚举类型 Enumeration **枚举类型**(`Enumeration`) 是一种**用户自定义类型**,包含一组 "**==具名常量==**"(称为**枚举值**)[^4]。 枚举类型中的每个 **==枚举值==**(enumerator)均为 "**整型字面值常量**",其本身就是一条 "**==常量表达式==**"。 C++中的**枚举类型**有两种: - **未限定作用域的枚举类型**(unscoped enumeration):由 `enum` 关键字声明 - **==限定作用域==的枚举类型**(scoped enumeration):由 **==`enum class`==** 关键字声明(C++11 起),也称 "**==枚举类==**" ```cpp // 枚举类型名称为Color, 枚举值有: RED, BLUE, GREEN. // 未限定作用域的枚举类型 enum Color { RED, BLUE, GREEN } Color c = RED; // 限定作用域的枚举类型 enum class Color { RED, BLUE, GREEN } Color c = Color::RED; // 引用枚举值必须声明类作用域 ``` ## 两种枚举类型的区别 > ❓「<font color="#c0504d">未限定作用域的枚举类型」与 「限定作用域的枚举类型」有什么区别?</font> ^rin6gm 区别如下: | | 访问枚举值 | 枚举值的==隐式类型转换== | 枚举值的==默认类型== | 前置声明中是否必须 "==明确==枚举值类型" | 是否可匿名 | | -------------------------------- | ------------------------------------------ | -------------- | ------------ | ----------------------- | ----- | | **未限定作用域**的`enum` | 直接访问 | ✔️ 支持隐式转为整型 | 无默认类型 | ✔️ 必须明确指定 | ✔️ | | **限定作用域**的 `enum class`<br>(枚举类) | 需要 "**作用域运算符** `::`"<br>(枚举值属于**枚举类**的成员) | ❌ 要求必须显式转换 | `int` | 可省略,省略时**默认 `int`** | ❌ | > [!note] 参见 [^1] [^2] > > > > ![[_attachment/02-开发笔记/01-cpp/类型相关/cpp-枚举类型 Enumeration.assets/IMG-cpp-枚举类型 Enumeration-A0CE8587E83E4FACBB1E72F6E79942B7.png|671]] ```cpp // 比较两种枚举类型 enum Fruit { APPLE, ORANGE, BANANA }; enum class Color { RED, BLACK, GREEN }; void comparison_exam() { // 1.未限定作用域枚举类型 // 1) 可直接访问枚举值 // 2) 其枚举值支持隐式类型转换 Fruit ff = APPLE; int f1 = BANANA; // 隐式类型转换 int f2 = ff; cout << APPLE << endl; cout << ff << endl; // 2.限定作用域枚举类型 // 1) 必须通过`枚举类型名::枚举值`访问枚举值 // 2) 其枚举值不支持隐式类型转换, 必须显式转换(如下) Color cc = Color::RED; int c1 = (int) Color::BLACK; // 必须显式类型转换 int c2 = (int) cc; // 必须显式类型转换 cout << (int) cc << endl; cout << (int) Color::RED << endl; } ``` <br> ## 枚举类型的前置声明 > 参见 [^2](Item10) - 「**无限域的枚举类型**」,仅当 "**==指定了枚举值的底层类型==**" 时才能进行前置声明; - 「**==限定作用域==的枚举类型**」总是可以进行前置声明。(因为其枚举值的**底层类型默认为 `int`**) > [!example] > > ```cpp > // 前置声明 > // 未限定作用域的枚举类型, 必须在前置声明中指定其枚举值的底层类型, 同时定义处也必须指明. > enum Dog : long long; > enum Dog : long long { Dog1, Dog2, Dog3 }; > > // 限定作用域的枚举类型, 可省略, 因为其默认类型为`int`. > enum class cat; // 默认int > enum class cat { cat1, cat2, cat3 }; // 默认int > > enum class Number : unsigned long long; // 如果声明中指定了底层类型, 则必须在定义处同样给出. > enum class Number : unsigned long long { NUM1, NUM2, NUM3 }; > ``` > <br> ## 匿名枚举类型 仅 "**==未限定作用域==的枚举类型**" 可以**省略类型名称**,称为 "**匿名枚举类型**"。 当 "**仅需要定义一组整型常量**",且**不需要在其他地方引用该枚举类型**(因而不需要其名称)时,可使用**匿名枚举类型**。 > [!example] > > ```cpp > // 匿名枚举类型(仅未限定作用域的枚举类型允许匿名) > enum { AA, BB, CC }; // 匿名枚举类型, 其枚举值可直接访问 > void anonymous_enum_exam() { > cout << AA << endl; > cout << BB << endl; > } > ``` > > <br><br> ## 枚举对象 枚举是一种 "类型",因此对于 "**==具名==枚举类型**",可以**定义并初始化==该类型的变量==**——**枚举对象**。 **枚举对象** 只能其**所属枚举类型**的 "**==枚举值==**" 进行**初始化、赋值**。 > [!caution] 不存在整型到 "枚举类型" 的隐式转换! => 不能用整型初始化或赋值给 "枚举对象" > [!example] > > ```cpp > enum Color { RED, BLUE, GREEN }; > Color cc = Color::RED; > cc = Color::BLUE; > > // Color cc = 0; // error > // cc = 2; // error > ``` > > <br><br> # 枚举值 Enumerator 枚举类型中的每个 **==枚举值==**(enumerator),其类型为 "**整型字面值常量**"。 每个枚举值本身都是 "**常量表达式**",可用**任何常量表达式==初始化其值==**。 ### 枚举值的值 枚举类型中,**各个枚举值对应的值==默认从 0 开始,依次加 1==**。 可以为每个枚举值**显式指定初始值**,未指定值的成员其值等于 "**==前一项枚举值的值 + 1==**" 。 ```cpp enum Color { RED, BLUE = 4, GREEN}; // RED==0, BLUE==4, GREEN==5 ``` ### 枚举值的底层类型 - 未限定作用域的枚举类型: 其**枚举值无默认的底层类型** ,由编译器决定。 - **限定作用域**的枚举类型: 其**枚举值的类型==默认为 `int`==**; 可在枚举类型名称后通过 `:` 指定每个**枚举值的默认类型**,如下所示: ```cpp enum Dog : int { DG1, DG2, DG3 }; enum class intTypes : unsigned long long { // 指定枚举值类型为ULL charType = 255, shortType = 65535, intType = 65535, longType = 4294967295UL, llongType = 18446744073709551615ULL }; ``` <br> # 枚举值使用示例 > 参见 [^1] [^3] ```cpp // 示例1: 可定义枚举类型的constexpr变量: enum class intTypes { charType = 8, shortType = 16, intType = 32, longType = 32, long_longType = 63 }; constexpr intTypes charbits = intTypes::charType; // 示例2: 可将枚举值作为switch语句中的case标签 enum class LogLevel { DEBUG = 0, INFO, WARN, ERROR, FATAL }; void func (LogLevel key) { switch(key) { case LogLevel::DEBUG : { ... } break; case LogLevel::INFO : { ... } break; ... } } // 示例3: 可将枚举值用于定义静态数组大小. struct MyClass { enum { INT_ARRAY_SIZE = 32, BUFF_SIZE = 1024 }; int array[INT_ARRAY_SIZE]; char buffer[BUFF_SIZE]; }; // 示例4: 枚举值用作非类型模版参数的实参 template <int V> void addN(int& val) { val += V; } enum { VAL1 = 5, VAL2 = 15 }; int main() { int arr[5] { 1, 2, 3, 4, 5 }; for_each(begin(arr), end(arr), addN<VAL1>); for_each(begin(arr), end(arr), addN<VAL2>); return 0; } // 示例5: 可在类中初始化 "枚举类型" 的静态数据成员 enum class MyEnum { KT = 5, DG = 6}; class MyClass { public: MyClass() = default; private: MyEnum mem = MyEnum::DG; }; ``` <br><br> # 参考资料 # Footnotes [^1]: 《C++程序设计语言》P188-189 [^2]: 《Effective Modern C++》Item 10 [^3]: 《C++ Primer》P737 [^4]: 《C++ Primer》P736-P739