枚举(enum)就是给一串整数起名字。 程序里到处是"只有几个固定取值"的东西:星期一到星期日、红绿蓝、订单的"待付款/已付款/已发货/已完成"。用 1 2 3 4 表示,写着写着就忘了哪个是哪个;用 enum 定义成 MON、TUE,代码自己会说话。它不增加什么功能,只是让代码可读、可检查。
这是第八章的最后一块拼图,也是 2.2(常量)那条线的延伸:2.2 我们学了 const 和 #define 来表示"固定的值",但面对"一组相关的取值"时,它们都不够用——const int MON = 0; const int TUE = 1; ... 写七行还容易重复值。enum 一次定义一组,还自动编号。另外它和 4.4 的 switch 是天然搭档:枚举当开关,switch 当分支,配合起来可读性最高。
一、先看"没有枚举"的痛
假设要写一个"判断今天要不要上班"的程序:
int day = 3; // 3 是星期几?—— 没人知道
if (day == 6 || day == 7) {
cout << "休息" << endl;
} else {
cout << "上班" << endl;
}
问题:
- 3 是什么?只有写代码的人知道,读的人得去翻注释(如果注释还在的话);
- 6 和 7 是周六周日吗?如果是"从 0 开始"的编号呢?——记错一位,逻辑全反;
- 编译器帮不上任何忙:写 day = 99; 它照过。
改成枚举:
enum Weekday { MON, TUE, WED, THU, FRI, SAT, SUN };
Weekday day = WED;
if (day == SAT || day == SUN) {
cout << "休息" << endl;
}
一眼就懂,而且写错了编译器会报错(day = 99; 在 C++ 里不允许)。
二、枚举到底做了什么
一句话:枚举给一组整数起了名字。
enum Weekday { MON, TUE, WED, THU, FRI, SAT, SUN };
// 0 1 2 3 4 5 6 ← 自动编号(默认从 0 开始)
所以 WED 就是一个写起来更好看的 2。底层仍然是整数,比较、传参、存数组全都和整数一样。
"枚举"这个名字的由来:把可能的取值 一个一个列举出来——enum 就是 enumeration(列举) 的缩写。
三、它最擅长的三类场景
| 场景 | 例子 |
|---|---|
| 状态机 | enum State { IDLE, RUNNING, PAUSED, STOPPED }; |
| 固定选项 | enum Color { RED, GREEN, BLUE };、enum Level { LOW, MID, HIGH }; |
| 业务枚举 | enum OrderStatus { UNPAID, PAID, SHIPPED, DONE }; |
判断标准:"这个东西的取值,能不能一个个数出来?"——能,就该考虑用枚举。
反例:年龄、成绩、价格——这些取值太多太连续,用
int/double就行,枚举不适合(总不能在源码里写 0 到 120 的枚举吧)。
四、和前面学过的"常量"对一对
| 做法 | 写法 | 优点 | 缺点 |
|---|---|---|---|
| 字面量 | day == 6 |
最短 | 没人知道 6 是什么 |
const |
const int SAT = 6; |
有名字 | 一组要写好多个,还可能写重复值 |
#define |
#define SAT 6 |
有名字 | 纯文本替换,没有类型 |
enum |
enum Weekday { ..., SAT, SUN }; |
一次定义一组、自动编号、有类型 | 只能表示整数 |
结论:能数出来的固定集合,就用 enum;单个固定值,用 const。(#define 定义常量在 2.2.5 已经建议淘汰。)
五、本节三篇的分工
| 小节 | 内容 |
|---|---|
| 8.6.1 枚举类型的声明 | 怎么写(默认编号、自定义编号) |
| 8.6.2 枚举类型变量 | 怎么用(赋值、输入输出的限制) |
| 8.6.3 枚举类型的运算 | 比较、计算、配合 switch(含实例06) |
六、动手 Demo:一眼看出枚举的可读性
#include <iostream>
using namespace std;
enum Weekday { MON, TUE, WED, THU, FRI, SAT, SUN };
enum OrderStatus { UNPAID, PAID, SHIPPED, DONE };
int main() {
// ① 枚举值就是整数
cout << "MON = " << MON << ", WED = " << WED << ", SUN = " << SUN << endl;
// ② 用名字写逻辑,读起来像自然语言
Weekday today = WED;
if (today == SAT || today == SUN)
cout << "今天休息" << endl;
else
cout << "今天上班" << endl;
// ③ 枚举值可以当数组下标(它就是整数)
const char *names[7] = {"周一","周二","周三","周四","周五","周六","周日"};
cout << "今天是 " << names[today] << endl;
// ④ 和 switch 配合(4.4 的搭档)
OrderStatus st = SHIPPED;
switch (st) {
case UNPAID: cout << "待付款" << endl; break;
case PAID: cout << "已付款" << endl; break;
case SHIPPED: cout << "已发货" << endl; break;
case DONE: cout << "已完成" << endl; break;
}
return 0;
}
看到什么算成功:
MON = 0, WED = 2, SUN = 6
今天上班
今天是 周三
已发货
三个看点:
- 枚举值直接当整数用(MON 打印出 0);
- names[today] 直接当下标——因为 today 本质是 2;
- switch 里写 case SHIPPED: 比 case 2: 清楚一百倍。
30 秒实验:把 Weekday today = WED; 改成 today = SUN;,看输出变成"休息"。再试着写一句 today = 99;——编译会报错。这就是枚举的另一个好处:编译器开始帮你把关了(8.6.2 会详细讲这条限制)。
七、常见误区
- "枚举是字符串"——不是。枚举值就是整数,
MON等于0。"名字"只存在于源码里,编译后只剩数字(所以运行时没法打印出WED这个词,除非自己准备名字表)。 - "枚举只是好看,没什么实际作用"——它有两个实打实的好处:① 可读性(
day == SATvsday == 5);② 类型检查(C++ 里不能随便把整数赋给枚举变量,写错会报错)。 - "枚举只能表示星期/颜色这种小东西"——状态机、业务状态、错误码都是它的主场。只要能一个个数出来,就该考虑枚举。
- "枚举定义可以省掉类型名"——可以(类似匿名结构体),但那样就只能用那几个常量、没法声明该类型的变量。除非只想要一组命名常量,否则都写上类型名。
自测三问(点开对答案)
1. 枚举的本质是什么?——**给一组整数起名字**。默认从 0 开始依次 +1。 2. 枚举比 `const` 定义一组常量的优势在哪?——**一次定义一组、自动编号**(不会写重复值)、**有类型检查**(不能随便赋别的整数)。 3. 什么样的数据适合做成枚举?——**取值能一个个列举出来的**(星期、状态、颜色、级别);取值太多、太连续的(年龄、价格)不适合。- ☐ 8.6.1 枚举类型的声明(下一节)
相关笔记
- 共用体的特点 —— 上一节:共用体三条特点
- 枚举类型的声明 —— 下一节:怎么写(含自定义编号)
- 其他常量 —— const 与 #define 的老对比
- switch语句 —— 枚举的天然搭档
- 🔷 C++基础 · 索引 —— 全课路线图