3、流程控制:if/else、switch、循环(for/while/do-while)、break/continue、嵌套与常见算法示例
流程控制是C++语言中组织程序执行逻辑的核心机制,它决定了代码语句的执行顺序与分支路径。对于Qt应用程序而言,无论是事件循环中的信号槽分发、界面状态切换,还是后台数据解析与算法实现,都离不开对流程控制语句的精准运用。本章将系统讲解条件判断、多路分支、三种循环结构以及跳转语句的语法规则与适用场景,并通过对比表格和综合案例,帮助学习者建立从语法到工程实践的完整认知。
3.1 条件判断:if/else 与 switch 的核心原理
条件判断是程序根据运行时数据做出决策的基础。C++提供了两种主要的多路分支语法:if/else if/else 用于处理范围型或复杂逻辑条件,而 switch 适用于对整型或枚举类型的精确匹配。理解两者的差异对于编写可读性高、性能优良的代码至关重要。
3.1.1 if/else 的判定逻辑与书写规范
if 语句根据括号内表达式的布尔值决定执行路径。当条件表达式的结果为 true 时执行 if 块,否则跳过并检查下一个 else if,若所有条件均不满足则执行 else 块。在实际开发中,需要注意以下几点:
- 条件顺序:应将最可能命中或优先级最高的条件放在前面,避免不必要的判断开销。
- 边界值处理:使用
>=、<=而非>、<来避免边界遗漏。 - 代码块:即使只有一条语句,也建议使用花括号
{},防止后续维护时引入逻辑错误。
// 示例:根据分数划分等级
int score = 85;
if (score >= 90) {
// 优秀
} else if (score >= 75) {
// 良好
} else if (score >= 60) {
// 及格
} else {
// 不及格
}
3.1.2 switch 的匹配机制与 fallthrough 陷阱
switch 语句将控制表达式的结果与各 case 标签逐一比较,匹配成功后从该标签处开始执行,直到遇到 break 或函数结束。若省略 break,程序会继续执行下一个 case 块,这种现象称为 fallthrough(穿透)。虽然 C++ 允许这种写法,但极易引发逻辑错误,除非刻意为之(如多个 case 共享同一处理逻辑),否则必须显式添加 break。
// 正确使用 switch
char grade = 'B';
switch (grade) {
case 'A':
// 处理 A
break;
case 'B':
// 处理 B
break;
default:
// 默认处理
break;
}
3.2 循环结构:for、while 与 do-while 的适用场景
循环用于重复执行一段代码直到满足终止条件。C++ 提供三种循环语法,它们在初始化、条件判断和迭代更新的时机上存在差异,选择合适的循环结构能显著提升代码清晰度。
| 循环类型 | 语法特点 | 执行次数 | 典型应用场景 |
|---|---|---|---|
| for | 初始化; 条件; 更新 集中在头部 | 先判断,后执行 | 已知迭代次数(如遍历数组、QList) |
| while | 仅条件判断在头部 | 先判断,后执行(可能0次) | 条件依赖运行时状态(如读取文件直到EOF) |
| do-while | 条件判断在尾部 | 至少执行1次 | 需要先执行再判断(如菜单显示) |
3.2.1 for 循环的计数与迭代器模式
在 Qt 开发中,for 循环常与容器遍历结合。C++11 引入了基于范围的 for 循环(range-based for),极大简化了遍历语法。但需要注意,若在循环体内需要修改容器大小(如删除元素),必须使用传统迭代器方式,否则会导致迭代器失效。
// 传统 for 遍历 QList
QList<int> list = {1, 2, 3, 4};
for (int i = 0; i < list.size(); ++i) {
qDebug() << list[i];
}
// 基于范围的 for(只读)
for (int value : list) {
qDebug() << value;
}
// 基于范围的 for(修改元素)
for (int &value : list) {
value *= 2;
}
3.2.2 while 与 do-while 的边界控制
while 循环在进入循环体之前检查条件,因此可能一次都不执行。而 do-while 至少执行一次,适用于“先执行后判断”的场景,例如用户输入验证:先读取输入,再判断是否合法。在 Qt 的事件处理中,while 循环常用于处理待处理事件,但必须注意避免死循环导致界面卡死。
// do-while 示例:确保输入非空
QString input;
do {
input = QInputDialog::getText(nullptr, "输入", "请输入名称:");
} while (input.isEmpty());
3.3 break/continue 与嵌套循环的实战技巧
break 用于立即终止当前循环(或 switch),而 continue 跳过本次迭代的剩余语句,直接进入下一次迭代。在嵌套循环中,break 只能跳出最内层循环,若需跳出多层,可使用标志变量或 goto(不推荐)。
3.3.1 嵌套循环的复杂度控制
嵌套循环常用于二维数组遍历、矩阵运算或组合枚举。时间复杂度为 O(n²) 或更高,在数据量较大时需谨慎优化。常见优化手段包括:将不变条件提取到外层、使用 break 提前终止内层循环、或改用其他算法(如哈希表)。
// 查找二维数组中的目标值
int matrix[3][3] = {{1,2,3},{4,5,6},{7,8,9}};
int target = 5;
bool found = false;
for (int i = 0; i < 3 && !found; ++i) {
for (int j = 0; j < 3; ++j) {
if (matrix[i][j] == target) {
found = true;
break; // 跳出内层循环
}
}
}
3.3.2 continue 在数据过滤中的应用
continue 在循环中用于跳过不符合条件的元素,避免深层嵌套的 if。例如,在处理传感器数据时,过滤掉无效读数:
QVector<double> rawData = {0.5, -1.0, 2.3, 99.9, 1.2};
QVector<double> validData;
for (double value : rawData) {
if (value < 0 || value > 100) {
continue; // 跳过无效数据
}
validData.append(value);
}
QtConcurrent 或 QThread 将任务移至后台线程。
3.4 综合案例:基于流程控制的成绩统计与等级评定系统
本案例将综合运用条件判断、循环和跳转语句,实现一个学生成绩统计工具。该工具接收一组分数,计算平均分、最高分、最低分,并根据平均分评定等级。此场景在教务管理系统或嵌入式设备自检中具有典型性。
3.4.1 需求分析与输入数据
假设某班级有 10 名学生,分数存储于 QVector<int> 中。初始数据为:{78, 92, 65, 88, 70, 59, 83, 96, 72, 61}。目标输出:平均分(保留一位小数)、最高分、最低分、等级(A: ≥90, B: ≥80, C: ≥70, D: ≥60, F: <60)。
3.4.2 代码实现与流程拆解
#include <QVector>
#include <QDebug>
void analyzeScores(const QVector<int>& scores) {
if (scores.isEmpty()) {
qWarning() << "成绩列表为空!";
return;
}
int sum = 0;
int maxScore = scores[0];
int minScore = scores[0];
// 使用 for 循环遍历并计算统计量
for (int score : scores) {
sum += score;
if (score > maxScore) maxScore = score;
if (score < minScore) minScore = score;
}
double average = static_cast<double>(sum) / scores.size();
// 使用 if/else 评定等级
char grade;
if (average >= 90) grade = 'A';
else if (average >= 80) grade = 'B';
else if (average >= 70) grade = 'C';
else if (average >= 60) grade = 'D';
else grade = 'F';
qDebug() << "平均分:" << QString::number(average, 'f', 1);
qDebug() << "最高分:" << maxScore;
qDebug() << "最低分:" << minScore;
qDebug() << "等级:" << grade;
}
3.4.3 执行结果与复盘
运行上述代码,输出结果为:平均分 76.4,最高分 96,最低分 59,等级 C。该案例展示了流程控制语句的协同工作:for 循环完成数据遍历,if 条件更新极值,if/else 链完成等级映射。复盘要点:
- 边界处理:空列表检查避免了除零错误。
- 类型转换:
static_cast<double>防止整数除法截断。 - 可扩展性:若需增加“优秀率”统计,只需在循环内添加计数器。
3.5 常见误区与总结复盘
流程控制语句虽然语法简单,但在实际工程中容易陷入以下误区,需要特别注意:
| 误区类型 | 错误示例 | 后果 | 正确做法 |
|---|---|---|---|
| switch 缺少 break | case 1: ... case 2: ... | 逻辑穿透,执行多个分支 | 每个 case 末尾添加 break |
| 循环条件恒真 | while(1) 且无退出路径 | 死循环,程序卡死 | 确保循环体内有退出条件 |
| 浮点数相等比较 | if (average == 75.0) | 精度问题导致判断失败 | 使用范围比较或 epsilon |
| 嵌套过深 | if 内嵌 if 内嵌 for | 可读性差,维护困难 | 提取函数或使用卫语句 |