枚举值可以比较(== != < > <= >= 全都能用,因为底层是整数)、可以做算术(但结果变成 int,要存回枚举变量得强转)、可以当数组下标、最适合配 switch。教材实例06就是"枚举值的比较运算"。一句总结:枚举就像"起了名字的整数",所以整数能干的它基本都能干;但"取名字"的意义在于可读性和类型安全,别为了省事又退回写数字。
8.6.2 讲了"能赋值、能比较"的基本用法,本节把运算部分补齐。这里会反复回到两个老家:3.1.2 的关系运算符(比较)和 4.4 的 switch(多分支)——枚举 + switch 是 C++ 里可读性最高的一对搭档,也是本章最实用的一个组合。
一、比较运算:最常用,也最安全
enum Weekday { MON, TUE, WED, THU, FRI, SAT, SUN };
Weekday a = MON, b = FRI;
if (a == b) cout << "同一天" << endl;
if (a != b) cout << "不是同一天" << endl;
if (a < b) cout << "a 的编号更小" << endl; // 靠底层的 0 和 4 比较
六个关系运算符全都能用:== != < > <= >=。
为什么能比大小? 因为枚举值本质是整数(8.6)。MON 是 0,FRI 是 4,所以 MON < FRI 成立。
⚠️ 但要注意语义:
MON < FRI这个比较"合法但未必有意义"——星期并不是"谁大谁优先"。比大小适合用在这种枚举上:enum Level { LOW, MID, HIGH };、enum Priority { P3, P2, P1 };——这些"等级"类枚举,比大小才有意义。
二、算术运算:结果会变成 int
Weekday today = WED; // 2
today + 1 // 3 —— 类型是 int,不是 Weekday
today++ // ⚠️ 合法,但……
规则:枚举参与算术后,结果类型是 int。所以要存回枚举变量,必须强转:
Weekday tomorrow = (Weekday)(today + 1); // 2 → 3,合法
这条正是"枚举的安全带":编译器不会让你悄悄把 int 存回枚举变量(8.6.2)。
⚠️ 更要注意边界:
Weekday last = SUN; // 6
Weekday bad = (Weekday)(last + 1); // 7 —— 越出枚举范围!
7 不在 Weekday 的定义里。编译器不报错(你强转了),但这个值是"未定义"的——拿它去比较、当数组下标都会出问题(和 7.1.2 的数组越界一样阴)。
安全做法:转之前先判断范围:
if (today < SUN) tomorrow = (Weekday)(today + 1);
else tomorrow = MON; // 绕回周一
三、遍历枚举:用 int 循环
for (int i = MON; i <= SUN; i++) {
cout << i << " ";
}
// 输出:0 1 2 3 4 5 6
要点:
- 循环变量用 int(不是 Weekday,因为 i++ 的结果是 int);
- i <= SUN 里的 SUN 会被当成 6;
- 需要枚举类型的值时就强转:Weekday d = (Weekday)i;。
这个技巧只在"编号连续"时好用(默认编号、或只给第一个赋值的情况)。如果编号跳跃(= 1, 2, 4, 8),就不能这样遍历了——只能把值一个个列出来。
四、和 switch 配合:本章最漂亮的组合
enum OrderStatus { UNPAID, PAID, SHIPPED, DONE };
void show(OrderStatus st) {
switch (st) {
case UNPAID: cout << "待付款" << endl; break;
case PAID: cout << "已付款" << endl; break;
case SHIPPED: cout << "已发货" << endl; break;
case DONE: cout << "已完成" << endl; break;
default: cout << "未知状态" << endl; break;
}
}
为什么这是最佳搭配?
| 好处 | 说明 |
|---|---|
| 读得懂 | case SHIPPED: 比 case 2: 清楚一百倍 |
| 不怕改编号 | 哪天把 SHIPPED 的编号改了,switch 一个字不用动 |
| 不容易漏 | 看着枚举列表,就知道该写几个 case |
default 兜底 |
万一传进来一个强转出来的野值,也能接住 |
注意:
switch的case里只能写常量(4.4 的老规矩),而枚举值就是常量,正好合适。
五、动手 Demo(实例06 枚举值的比较运算)
教材实例06就是"枚举值的比较运算"。这里做成一个等级评定 + 优先级比较 + 状态机的完整小工具:
#include <iostream>
using namespace std;
enum Level { LOW = 1, MID, HIGH }; // 等级:1~3
enum Weekday { MON, TUE, WED, THU, FRI, SAT, SUN }; // 星期:0~6
const char *levelName(Level l) {
switch (l) {
case LOW: return "低";
case MID: return "中";
case HIGH: return "高";
}
return "未知";
}
const char *weekName(Weekday w) {
const char *n[7] = {"周一","周二","周三","周四","周五","周六","周日"};
return n[w];
}
int main() {
// === ① 比较运算 ===
Level a = LOW, b = HIGH;
cout << "=== 比较 ===" << endl;
cout << "a = " << levelName(a) << "(" << a << "), b = " << levelName(b) << "(" << b << ")" << endl;
cout << "a == b ? " << (a == b ? "是" : "否") << endl; // 4.3 的三目运算符
cout << "a < b ? " << (a < b ? "是" : "否") << endl;
cout << "a >= b ? " << (a >= b ? "是" : "否") << endl;
// === ② 算术运算(结果转 int) ===
cout << "=== 算术 ===" << endl;
Level c = MID; // 2
cout << "c = " << c << ",c + 1 = " << (c + 1) << "(类型是 int)" << endl;
Level up = (Level)(c + 1); // 强转回枚举
cout << "升级后:" << levelName(up) << endl;
// === ③ 越界的危险 ===
cout << "=== 边界 ===" << endl;
Level top = HIGH;
cout << "已到最高级(" << top << "),再 +1 会变成 " << (top + 1)
<< " —— 这已经超出枚举范围了(危险)" << endl;
// === ④ 遍历星期 ===
cout << "=== 遍历 ===" << endl;
for (int i = MON; i <= SUN; i++)
cout << weekName((Weekday)i) << " ";
cout << endl;
// === ⑤ 比较决定休息日 ===
cout << "=== 休息日判断 ===" << endl;
Weekday today = SAT;
if (today >= SAT) cout << weekName(today) << " 休息" << endl; // 靠编号比大小
else cout << weekName(today) << " 上班" << endl;
return 0;
}
看到什么算成功:
=== 比较 ===
a = 低(1), b = 高(3)
a == b ? 否
a < b ? 是
a >= b ? 否
=== 算术 ===
c = 2,c + 1 = 3(类型是 int)
升级后:高
=== 边界 ===
已到最高级(3),再 +1 会变成 4 —— 这已经超出枚举范围了(危险)
=== 遍历 ===
周一 周二 周三 周四 周五 周六 周日
=== 休息日判断 ===
周六 休息
四个看点:
- 比较运算符全都能用(== < >=),因为它们最终比的是整数;
- c + 1 的结果是 int,要变回 Level 必须强转;
- HIGH + 1 = 4 已经越界——强转能编过,但这个值不代表任何状态,这是枚举最阴的坑;
- today >= SAT 判断休息日——用编号顺序表达"等级/优先级"是枚举比大小的正当用法。
注意 ⑤ 这种"靠编号大小判断"的写法只有约定好"SAT/SUN 排在最后"才成立。更稳的写法是
if (today == SAT || today == SUN)——不依赖编号顺序,改编号也不会坏。
30 秒实验:把 enum Weekday { MON, TUE, ... } 改成 enum Weekday { SUN, MON, TUE, WED, THU, FRI, SAT };(把周日挪到最前)。⑤ 那句 today >= SAT 立刻判断错——这就是"依赖编号顺序"的代价。改成 today == SAT || today == SUN 就不会有这个问题。
六、常见误区
- "枚举不能比大小"——能,六个关系运算符都支持(靠底层整数)。但要问一句"这个比大小有意义吗":等级类有意义,星期类最好别依赖顺序。
- "
today + 1的类型还是枚举"——是int。存回枚举变量要强转,这是编译器在保护你。 - "强转后一定能用"——不一定。
(Weekday)(SUN + 1)得到7,超出枚举范围,值无意义(和数组越界同样阴险)。转之前先判断边界。 - "遍历枚举时循环变量也用枚举类型"——不行(
i++的结果是int)。用int做循环变量,需要时再强转成枚举。 - "
switch里可以写case today:"——不行。case只收常量,枚举值正好是常量 ✅,但变量不行 ❌。
自测三问(点开对答案)
1. `enum Level { LOW = 1, MID, HIGH };` 里,`HIGH < LOW` 的值是?为什么合法?——`false`(因为 `HIGH` = 3、`LOW` = 1)。合法是因为**枚举底层是整数**,比较实际比的是数值。 2. `Level up = MID + 1;` 能编译过吗?怎么改?——不能(右值是 `int`)。改成 `Level up = (Level)(MID + 1);`。 3. 为什么说"枚举 + `switch`"是可读性最高的组合?——`case SHIPPED:` 比 `case 2:` 清楚;枚举编号改了 `switch` 不用改;看枚举列表就知道该写几个分支。- ☐ 8.7 小结(下一节)
相关笔记
- 枚举类型变量 —— 上一节:三条能与不能
- 小结 —— 下一节:本章知识地图
- switch语句 —— 最佳搭档
- 关系运算符 —— 六个比较运算符
- 🔷 C++基础 · 索引 —— 全课路线图