C++ practical course · lesson 09

C++ 条件判断、枚举与菜单状态

条件判断让程序根据不同情况选择行动。本课不仅学习 if 和 switch 的写法,更重要的是把复杂规则整理成不会互相冲突的清晰顺序。

Easy explanation

条件判断像路口的交通规则。

红灯停、绿灯行,看起来只有两个分支;但真实路口还要考虑行人、转弯箭头和紧急车辆。程序里的规则也会从一条 if 逐渐变复杂。若想到一条就随手加一条,后面的条件可能永远执行不到,也可能同一输入同时满足多条规则。

清晰的判断要先处理最特殊、最严格的情况,再处理普通情况。例如票价系统先判断年龄是否有效,再判断免票年龄,接着判断儿童或老人优惠,最后才是成人原价。把规则按优先级写出来,比不停增加括号更重要。

目标 1使用 if、else if 和 else 表达互斥规则
目标 2正确组合 &&、|| 和 !
目标 3用 switch 处理固定菜单选项
目标 4用 enum class 表示有限状态
C++ 条件判断、枚举与菜单状态通俗学习插图
先画出分支顺序,再把每条路径翻译成条件表达式。

Core concepts

先把四个核心知识点说清楚。

01

互斥分支

if (...) { } else if (...) { } else { }

整条结构只会执行一个分支,适合年龄区间、成绩等级等互不重叠的分类。

02

逻辑组合

age >= 6 && age <= 17

&& 要求两边都真,|| 只需一边真,! 负责取反。复杂表达式要用有意义的 bool 变量拆开。

03

固定选项

switch (choice) { case 1: ... }

switch 适合整数、字符和枚举形成的固定选项。每个 case 通常用 break 防止继续落入下一项。

04

有限状态

enum class State { Menu, Playing, Exit };

枚举给状态取名字,避免用 0、1、2 这种难懂的魔法数字表示程序阶段。

bool isTeen = age >= 13 && age <= 19;

先把复杂条件命名,后面的 if 会更像自然语言。

if (score < 0 || score > 100)

先排除无效范围,再进行成绩分级。

case 0: state = State::Exit; break;

固定菜单用 switch 能让每个选项边界清楚。

if (!isLoggedIn) return;

提前处理不能继续的情况,可以减少多层缩进。

Complete examples

两个例子,把知识变成能运行的程序。

Example 1

例子一:按优先级计算电影票价

票价规则要先决定基础价格,再应用折扣。若把所有情况塞进一个巨大条件,后续修改会很危险。这里先验证年龄,再确定年龄票价,最后统一处理会员优惠。

把每一个阶段的结果保存在 price 中,便于输出和调试。规则顺序就是算法的一部分。

年龄、会员和周末规则
double price = 12.0;

if (age < 0 || age > 120)
{
    cout << "年龄无效\n";
    return 1;
}
else if (age < 6)
    price = 0;
else if (age <= 17)
    price = 7;
else if (age >= 65)
    price = 8;

if (isWeekend && price > 0)
    price += 2;
if (isMember && price > 0)
    price *= 0.9;

cout << "票价:" << price << " 欧元\n";
输入:14 岁,周末,会员儿童票基础价:7周末加价后:9会员价:8.1 欧元

Example 2

例子二:用枚举控制游戏菜单

菜单、游戏和退出是三个有限状态。使用枚举后,代码直接表达“当前处于菜单”,而不是让读者猜数字 1 的含义。

switch 负责根据当前状态执行对应逻辑,每次执行都决定下一状态。这个简单结构就是有限状态机的雏形。

状态明确的主循环
enum class State { Menu, Playing, Exit };
State state = State::Menu;

while (state != State::Exit)
{
    switch (state)
    {
    case State::Menu:
        cout << "1 开始,0 退出:";
        int choice;
        cin >> choice;
        state = choice == 1 ? State::Playing : State::Exit;
        break;
    case State::Playing:
        cout << "游戏进行一回合\n";
        state = State::Menu;
        break;
    case State::Exit:
        break;
    }
}
1 开始,0 退出:1游戏进行一回合1 开始,0 退出:0

Algorithm thinking

用决策表消除遗漏和冲突。

规则超过三条时,先画表格:每一列写一个条件,每一行写一种组合,最后一列写结果。表格能暴露没有覆盖的情况,也能发现两条规则对同一输入给出不同答案。完成表格后,再按优先级转成代码。

边界值最容易出错。年龄 17 和 18、分数 59 和 60、余额刚好等于价格,都应该单独测试。不要只测明显属于区间中间的值。对于有限状态,列出每个状态允许进入哪些状态,禁止的转换应给出提示而不是悄悄执行。

  1. 1
    列出规则

    用完整句子写下所有条件、结果和优先级。

  2. 2
    处理无效情况

    先拒绝范围外数据,让后续分支只面对合法输入。

  3. 3
    从特殊到普通

    先写最严格或最少见的情况,最后用 else 接住普通情况。

  4. 4
    测试边界

    每个比较符左右都准备测试值,确认大于和大于等于没有写反。

Common mistakes

这些错误很常见,学会自己排查。

1

把 = 写成 ==

赋值会改变变量,比较才会产生真假。编译警告常能帮助发现这种错误。

2

区间条件写成数学形式

C++ 不能写 0 <= x <= 100,要拆成 x >= 0 && x <= 100。

3

case 忘记 break

程序会继续执行下面的 case。若确实需要贯穿,应写注释明确说明。

4

分支顺序遮挡

先写 age >= 18,再写 age >= 65,老人条件永远到不了。应把更特殊的范围放前面。

本课通用调试法

先准备一个最小输入,只保留能够重现问题的几行数据;再在关键步骤输出变量值,确认程序究竟在哪一步偏离预期。编译错误从第一条开始处理,运行错误则比较“实际结果”和“期望结果”。修复后别只重跑原来的例子,还要增加空输入、边界值和错误输入,防止问题换一个形式再次出现。

Homework

第 9 课课后作业:从模仿到独立完成。

建议按顺序完成。前四题帮助巩固语法和基本操作,第五到第七题要求把多个知识点组合起来,第八题是小项目。每道题都要先写输入、处理、输出三行计划,再开始敲代码;程序运行后至少测试正常情况、边界情况和错误情况。

1

奇偶与符号

输入整数,分别判断正负零以及奇偶,输出完整句子,并测试最小的正数和负数。

2

成绩等级

把 0 到 100 转成 A 到 F,先拒绝无效成绩,重点测试 59、60、89、90 和 100。

3

闰年判断

根据能被 4、100 和 400 整除的规则判断闰年,并解释为什么 1900 与 2000 结果不同。

4

三数最大值

不用 max 函数找出三个数中的最大值,并正确处理两个或三个数相等。

5

运费计算

根据重量、会员和加急状态计算运费。先写决策表,再写代码和六组测试。

6

自动售货机

用 switch 处理四种商品编号,检查余额,购买成功后输出找零。

7

角色状态机

设计 Idle、Running、Paused 和 Finished 状态,只允许合理转换,非法命令要提示。

8

影院购票项目

组合年龄、日期、会员、座位类型和优惠券规则,输出逐步价格明细与最终价格。

本课验收标准:能够解释每个分支的优先级;所有合法输入都有结果,所有非法输入都有提示;边界值测试覆盖每个区间交界;固定菜单至少正确使用一次 switch 或 enum class。

提交内容应包括源代码、三组测试输入与输出、一个曾经出现的错误及修复方法。最后请用自己的话解释本课最重要的概念;如果只能照着代码念,还需要再独立重写一次核心例子。

Lesson 09 summary

好的条件判断,是一份没有歧义的规则说明。

if / else&& / || / !switchenum class边界测试

下一课会让程序重复执行任务。条件决定“走哪条路”,循环决定“这条路走多少次”,两者结合后才能处理一批数据。