🔷 第八章 结构体与共用体
📄 一句话概括

枚举(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 会详细讲这条限制)。

七、常见误区

  1. "枚举是字符串"——不是。枚举值就是整数,MON 等于 0。"名字"只存在于源码里,编译后只剩数字(所以运行时没法打印出 WED 这个词,除非自己准备名字表)。
  2. "枚举只是好看,没什么实际作用"——它有两个实打实的好处:① 可读性(day == SAT vs day == 5);② 类型检查(C++ 里不能随便把整数赋给枚举变量,写错会报错)。
  3. "枚举只能表示星期/颜色这种小东西"——状态机、业务状态、错误码都是它的主场。只要能一个个数出来,就该考虑枚举。
  4. "枚举定义可以省掉类型名"——可以(类似匿名结构体),但那样就只能用那几个常量、没法声明该类型的变量。除非只想要一组命名常量,否则都写上类型名。
自测三问(点开对答案) 1. 枚举的本质是什么?——**给一组整数起名字**。默认从 0 开始依次 +1。 2. 枚举比 `const` 定义一组常量的优势在哪?——**一次定义一组、自动编号**(不会写重复值)、**有类型检查**(不能随便赋别的整数)。 3. 什么样的数据适合做成枚举?——**取值能一个个列举出来的**(星期、状态、颜色、级别);取值太多、太连续的(年龄、价格)不适合。

相关笔记