互斥分支
if (...) { } else if (...) { } else { }
整条结构只会执行一个分支,适合年龄区间、成绩等级等互不重叠的分类。
Easy explanation
红灯停、绿灯行,看起来只有两个分支;但真实路口还要考虑行人、转弯箭头和紧急车辆。程序里的规则也会从一条 if 逐渐变复杂。若想到一条就随手加一条,后面的条件可能永远执行不到,也可能同一输入同时满足多条规则。
清晰的判断要先处理最特殊、最严格的情况,再处理普通情况。例如票价系统先判断年龄是否有效,再判断免票年龄,接着判断儿童或老人优惠,最后才是成人原价。把规则按优先级写出来,比不停增加括号更重要。
Core concepts
if (...) { } else if (...) { } else { }
整条结构只会执行一个分支,适合年龄区间、成绩等级等互不重叠的分类。
age >= 6 && age <= 17
&& 要求两边都真,|| 只需一边真,! 负责取反。复杂表达式要用有意义的 bool 变量拆开。
switch (choice) { case 1: ... }
switch 适合整数、字符和枚举形成的固定选项。每个 case 通常用 break 防止继续落入下一项。
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";
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;
}
}
Algorithm thinking
规则超过三条时,先画表格:每一列写一个条件,每一行写一种组合,最后一列写结果。表格能暴露没有覆盖的情况,也能发现两条规则对同一输入给出不同答案。完成表格后,再按优先级转成代码。
边界值最容易出错。年龄 17 和 18、分数 59 和 60、余额刚好等于价格,都应该单独测试。不要只测明显属于区间中间的值。对于有限状态,列出每个状态允许进入哪些状态,禁止的转换应给出提示而不是悄悄执行。
用完整句子写下所有条件、结果和优先级。
先拒绝范围外数据,让后续分支只面对合法输入。
先写最严格或最少见的情况,最后用 else 接住普通情况。
每个比较符左右都准备测试值,确认大于和大于等于没有写反。
Common mistakes
赋值会改变变量,比较才会产生真假。编译警告常能帮助发现这种错误。
C++ 不能写 0 <= x <= 100,要拆成 x >= 0 && x <= 100。
程序会继续执行下面的 case。若确实需要贯穿,应写注释明确说明。
先写 age >= 18,再写 age >= 65,老人条件永远到不了。应把更特殊的范围放前面。
先准备一个最小输入,只保留能够重现问题的几行数据;再在关键步骤输出变量值,确认程序究竟在哪一步偏离预期。编译错误从第一条开始处理,运行错误则比较“实际结果”和“期望结果”。修复后别只重跑原来的例子,还要增加空输入、边界值和错误输入,防止问题换一个形式再次出现。
Homework
建议按顺序完成。前四题帮助巩固语法和基本操作,第五到第七题要求把多个知识点组合起来,第八题是小项目。每道题都要先写输入、处理、输出三行计划,再开始敲代码;程序运行后至少测试正常情况、边界情况和错误情况。
输入整数,分别判断正负零以及奇偶,输出完整句子,并测试最小的正数和负数。
把 0 到 100 转成 A 到 F,先拒绝无效成绩,重点测试 59、60、89、90 和 100。
根据能被 4、100 和 400 整除的规则判断闰年,并解释为什么 1900 与 2000 结果不同。
不用 max 函数找出三个数中的最大值,并正确处理两个或三个数相等。
根据重量、会员和加急状态计算运费。先写决策表,再写代码和六组测试。
用 switch 处理四种商品编号,检查余额,购买成功后输出找零。
设计 Idle、Running、Paused 和 Finished 状态,只允许合理转换,非法命令要提示。
组合年龄、日期、会员、座位类型和优惠券规则,输出逐步价格明细与最终价格。
本课验收标准:能够解释每个分支的优先级;所有合法输入都有结果,所有非法输入都有提示;边界值测试覆盖每个区间交界;固定菜单至少正确使用一次 switch 或 enum class。
提交内容应包括源代码、三组测试输入与输出、一个曾经出现的错误及修复方法。最后请用自己的话解释本课最重要的概念;如果只能照着代码念,还需要再独立重写一次核心例子。