《Hello C++》4.5 枚举

《Hello C++》4.5 枚举
0x3ff18a在前面学习了数组、结构体和共用体之后,我们已经接触了几种能够组织数据的复合类型。数组适合存放同类型的一组数据,结构体适合把不同类型但彼此相关的数据组织在一起,共用体则适合让多个成员共享同一块内存。而这一小节要介绍的枚举[1],则更像是一种“给有限的取值起名字”的工具。
有时候,程序里会出现一些只允许在固定范围内取值的内容,比如星期、方向、游戏状态、菜单选项、交通信号灯状态等。对于这类数据,如果直接用数字表示,程序会显得很难读;如果改用有意义的名字,代码就会清楚很多。枚举就是为这种需求准备的。
4.5.1 枚举的基本概念
枚举(enumeration)是一种用户自定义的数据类型,它允许你把一组有名字的整型常量组织在一起。换句话说,枚举类型定义了一组可能的取值,而这些取值通常都很有限。
例如,一个“星期”的概念通常只有 7 个值。与其在程序里写 1、2、3 这样不直观的数字,不如把它们定义成 Monday、Tuesday、Wednesday 之类的名字。这样一来,代码本身就会更容易理解。
1 | enum WeekDay |
这段代码定义了一个名为 WeekDay 的枚举类型。花括号里的每一个名字都叫做枚举值[2]。默认情况下,C++ 会从 0 开始给这些枚举值编号,所以 Monday 的值是 0,Tuesday 的值是 1,以此类推。
4.5.2 枚举变量的使用
定义好枚举之后,就可以像使用普通类型一样声明枚举变量。
1 |
|
在这段代码里,today 的类型是 WeekDay,它当前的值是 Wednesday。如果直接输出 today,很多编译环境下实际显示的是对应的整数值,也就是 2,因为枚举本质上还是一种整型类型。
这说明了一个很重要的特点:枚举的价值不在于它能表示数字,而在于它能让数字有名字。程序员看到 Wednesday,会比看到 2 更容易理解。
4.5.3 枚举与 switch
枚举和 switch 常常会一起使用,因为枚举的值本身就是一组有限的离散选项,而 switch 也正适合处理这种情况。
1 |
|
这段代码的意思很直观:如果信号灯是红灯,就停下;如果是黄灯,就等待;如果是绿灯,就前进。枚举把“状态”变成了有名字的值,switch 则负责根据这个值选择对应的处理方式。这种写法比直接拿 0、1、2 去判断要清楚得多,也更情况下也不容易写错。
4.5.4 枚举值的显式赋值
枚举值并不一定非要按默认顺序从 0 开始。你也可以给它们指定具体的数值。
1 | enum ErrorCode |
这样写以后,OK、FileNotFound 和 PermissionDenied 就分别对应了你指定的整数值。这个特性在和系统状态码、协议编号、错误码打交道时非常方便。如果没有显式指定后续值,编译器会在前一个值的基础上自动加 1。
1 | enum Level |
这里 Low 是 1,Medium 是 2,High 是 3。
4.5.5 传统枚举的特点与问题
传统枚举的优点很明显:写法简单、语义清楚、和 switch 配合自然。不过,它也有一些值得注意的地方。
首先,传统枚举的枚举值会“泄露”到外层作用域中。也就是说,如果你定义了多个枚举,它们的枚举值名字可能发生冲突。其次,传统枚举会隐式转换为整型,这有时会让类型检查不够严格。比如某些地方本来只想接受某个枚举类型,却可能意外接受了整数。这也是为什么现代 C++ 更推荐使用 enum class。
4.5.6 enum class
enum class 是 C++11 引入的强类型枚举[3],它比传统枚举更安全,也更不容易发生名字冲突。
1 | enum class Color |
和传统枚举不同,enum class 的枚举值不会直接暴露到外层作用域中。也就是说,你在使用时要写成:
1 | Color c = Color::Red; |
这样可以明确告诉读者:Red 属于 Color 这个枚举类型,而不是一个随处可见的普通名字。这带来的好处是显而易见的。第一,作用域更清晰;第二,类型检查更严格;第三,代码更不容易冲突。对于现代 C++ 来说,这是更推荐的写法。
4.5.7 枚举的适用场景
枚举特别适合表达“有限个、固定的、彼此独立的状态或选项”。比如:
- 星期几;
- 方向;
- 游戏状态;
- 菜单编号;
- 错误码;
- 交通信号灯状态。
当你发现某个变量只会在几个固定值之间切换时,就可以考虑用枚举来表达它。这样做能让代码更接近真实含义,也能减少使用魔法数字[4]带来的混乱。例如,下面这个例子就比直接写数字更容易理解:
1 | enum GameState |
以后看到 GameOver,你立刻就知道它表示“游戏结束”,而不是一个不明所以的数字 3。
4.5.8 小结
枚举是一种非常实用的类型,它把一组有限的值变成了有名字的常量。对于那些“只有几种固定状态”的问题,枚举往往比直接使用整数更清楚、更安全,也更容易维护。
如果说数组帮助我们管理“一组相同类型的数据”,结构体帮助我们组织“多个相关的数据项”,那么枚举就是帮助我们管理“有限的状态和选项”。它看起来很小,但在程序设计里却非常常见。后面学习流程控制时,你会发现枚举和 switch 的组合几乎是天生一对;而在更复杂的程序中,枚举也常常用来描述状态机、配置选项和错误码。







