C++教程 第5章 分支语句和逻辑运算符
第5章 分支语句和逻辑运算符
分支语句
if 语句的高级用法
复合条件
1 | // 使用逻辑运算符组合条件 |
嵌套 if 语句
1 | // 嵌套if语句 |
switch 语句的高级用法
枚举类型与 switch
1 | // 枚举类型与switch |
switch 语句中的变量定义
1 | // switch语句中的变量定义 |
逻辑运算符
逻辑运算符的优先级
| 运算符 | 优先级 | 结合性 |
|---|---|---|
| ! | 高 | 右到左 |
| && | 中 | 左到右 |
| || | 低 | 左到右 |
短路求值
逻辑运算符具有短路求值的特性,即当表达式的结果已经确定时,不再计算剩余的部分:
1 | // 短路求值示例 |
短路求值的应用
安全的空指针检查:
1
2
3
4// 安全地访问指针成员
if (ptr != nullptr && ptr->value > 0) {
// 只有当ptr不为空时才访问ptr->value
}条件函数调用:
1
2
3
4// 只有当条件满足时才调用函数
if (needUpdate && updateData()) {
// 执行更新后的操作
}层级检查:
1
2
3
4// 层级检查,确保每个环节都有效
if (user != nullptr && user->profile != nullptr && user->profile->address != nullptr) {
// 访问user->profile->address
}性能优化:
1
2
3
4// 先检查快速条件,再检查慢速条件
if (simpleCheck() && expensiveCheck()) {
// 只有当simpleCheck()为真时才执行expensiveCheck()
}
逻辑表达式的返回值
逻辑表达式返回布尔值,但在C++中,非布尔值也可以用于逻辑表达式:
1 | bool result1 = (5 > 3 && 2 < 4); // true |
非布尔值的逻辑表达式
- 整数类型:零值为
false,非零值为true - 指针类型:空指针为
false,非空指针为true - 浮点类型:零值为
false,非零值为true - 容器类型:空容器的布尔转换通常为
false,非空容器为true(C++20+)
1 | // 指针类型的逻辑表达式 |
逻辑表达式的性能优化
条件顺序优化:
- 将快速计算的条件放在前面
- 将可能性大的条件放在前面
- 将副作用小的条件放在前面
1
2
3
4
5
6
7
8
9// 优化前
if (expensiveCheck() && commonCondition()) {
// ...
}
// 优化后
if (commonCondition() && expensiveCheck()) {
// ...
}逻辑表达式的简化:
- 使用德摩根定律简化逻辑表达式
- 提取重复的条件到变量
- 使用布尔代数简化复杂表达式
1
2
3
4
5
6
7
8
9
10// 德摩根定律示例
// 原表达式
if (!(a && b)) {
// ...
}
// 简化后
if (!a || !b) {
// ...
}避免不必要的计算:
- 使用短路求值避免不必要的计算
- 缓存计算结果
- 避免在逻辑表达式中执行有副作用的操作
1
2
3
4
5// 避免重复计算
bool isValid = checkValidity();
if (isValid && processValidData()) {
// ...
}
现代C++中的逻辑表达式
nullptr的使用:
1
2
3
4// 现代C++推荐使用nullptr
if (ptr != nullptr) {
// ...
}std::optional的使用:
1
2
3
4
5// 使用std::optional进行空值检查
std::optional<int> value = getValue();
if (value) {
std::cout << "Value: " << *value << std::endl;
}std::variant的使用:
1
2
3
4
5// 使用std::variant进行类型检查
std::variant<int, double, std::string> var = 42;
if (std::holds_alternative<int>(var)) {
std::cout << "int value: " << std::get<int>(var) << std::endl;
}概念的使用(C++20+):
1
2
3
4
5
6// 使用概念进行编译时类型检查
template <typename T>
requires std::integral<T>
void processIntegral(T value) {
// ...
}
关系运算符
关系运算符的优先级
| 运算符 | 优先级 | 结合性 |
|---|---|---|
| <, <=, >, >= | 中高 | 左到右 |
| ==, != | 中 | 左到右 |
关系表达式的返回值
关系表达式返回布尔值:
1 | bool result1 = (5 > 3); // true |
关系运算符的安全性
整数比较:
- 有符号整数与无符号整数比较时要小心
- 避免整数溢出导致的比较错误
1
2
3
4
5
6
7
8// 有符号整数与无符号整数比较
int a = -1;
unsigned int b = 1;
if (a < b) { // 错误:a会被转换为无符号整数,变成很大的正数
std::cout << "a < b" << std::endl;
} else {
std::cout << "a >= b" << std::endl; // 会执行这里
}浮点数比较:
- 避免直接使用==和!=比较浮点数
- 使用容差进行比较
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 浮点数比较
double x = 0.1;
double y = 0.3 / 3.0;
// 错误:直接比较
if (x == y) {
std::cout << "x == y" << std::endl;
} else {
std::cout << "x != y" << std::endl; // 可能执行这里
}
// 正确:使用容差
const double EPSILON = 1e-9;
if (std::abs(x - y) < EPSILON) {
std::cout << "x approximately equals y" << std::endl;
}指针比较:
- 只有指向同一数组或对象的指针才能安全比较
- 空指针可以与任何指针比较
1
2
3
4
5
6
7
8// 指针比较
int arr[5] = {1, 2, 3, 4, 5};
int* p1 = &arr[0];
int* p2 = &arr[1];
if (p1 < p2) {
std::cout << "p1 points to an earlier element" << std::endl;
}
现代C++中的比较运算符
三路比较运算符(C++20+):
1
2
3
4
5
6
7
8
9
10// 使用三路比较运算符
auto result = a <=> b;
if (result < 0) {
std::cout << "a < b" << std::endl;
} else if (result == 0) {
std::cout << "a == b" << std::endl;
} else {
std::cout << "a > b" << std::endl;
}默认比较(C++20+):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 使用默认比较
struct Point {
int x;
int y;
// 默认生成所有比较运算符
auto operator<=>(const Point&) const = default;
};
Point p1{1, 2};
Point p2{3, 4};
if (p1 < p2) {
std::cout << "p1 < p2" << std::endl;
}太空船运算符的应用:
1
2
3
4
5
6
7
8
9
10
11
12
13// 自定义类型的比较
class Person {
public:
std::string name;
int age;
auto operator<=>(const Person& other) const {
if (auto cmp = name <=> other.name; cmp != 0) {
return cmp;
}
return age <=> other.age;
}
};
关系表达式的最佳实践
使用命名常量:
1
2
3
4
5
6
7// 使用命名常量提高可读性
const int MIN_AGE = 18;
const int MAX_AGE = 65;
if (age >= MIN_AGE && age <= MAX_AGE) {
// ...
}提取复杂条件:
1
2
3
4
5
6
7
8// 提取复杂条件到函数
bool isEligible(int age, bool hasLicense, bool hasInsurance) {
return age >= 18 && hasLicense && hasInsurance;
}
if (isEligible(userAge, userHasLicense, userHasInsurance)) {
// ...
}使用枚举提高可读性:
1
2
3
4
5
6
7
8
9
10// 使用枚举提高可读性
enum class Status {
Inactive,
Active,
Suspended
};
if (userStatus == Status::Active) {
// ...
}避免魔法数字:
1
2
3
4
5
6
7
8
9
10
11
12// 避免魔法数字
if (score >= 90) { // 90是魔法数字
// ...
}
// 更好的做法
const int PASSING_SCORE = 60;
const int EXCELLENT_SCORE = 90;
if (score >= EXCELLENT_SCORE) {
// ...
}
条件运算符
条件运算符的嵌套
1 | // 嵌套条件运算符 |
条件运算符与表达式
1 | // 条件运算符用于表达式 |
逻辑运算符的应用
1. 范围检查
1 | // 检查值是否在范围内 |
2. 多重条件检查
1 | // 检查用户名和密码 |
3. 状态检查
1 | // 检查系统状态 |
分支语句的最佳实践
1. 代码可读性
- 缩进:使用一致的缩进风格
- 空格:在运算符两侧、逗号后添加空格
- 括号:即使只有一条语句,也使用大括号包围
- 注释:为复杂的条件添加注释
2. 避免深度嵌套
- 提取函数:将复杂的嵌套代码提取为单独的函数
- 使用早期返回:对于简单的错误情况,使用早期返回
- 简化条件:将复杂的条件表达式分解为多个简单的条件
3. 条件表达式的简化
- 使用常量:将魔法数字替换为命名常量
- 使用枚举:将整数常量替换为枚举类型
- 使用布尔变量:将复杂的条件表达式赋值给有意义的布尔变量
4. switch 语句的最佳实践
- 包含default分支:处理所有未明确处理的情况
- 使用break语句:避免意外的穿透行为
- 保持case标签简洁:每个case标签只处理一个逻辑情况
- 考虑使用枚举:与枚举类型一起使用,提高类型安全性
常见错误和陷阱
1. 赋值运算符与相等运算符混淆
1 | // 错误:使用赋值运算符而不是相等运算符 |
2. 逻辑运算符与位运算符混淆
1 | // 错误:使用位运算符而不是逻辑运算符 |
3. 连续比较错误
1 | // 错误:连续比较 |
4. 浮点数比较错误
1 | // 错误:直接比较浮点数 |
5. 短路求值依赖
1 | // 错误:依赖短路求值的副作用 |
小结
本章介绍了C++中的分支语句和逻辑运算符,包括:
- 分支语句:if语句、switch语句的高级用法
- 逻辑运算符:&&、||、!的用法和短路求值特性
- 关系运算符:<、<=、>、>=、==、!=的用法
- 条件运算符:?:的嵌套使用
- 分支语句和逻辑运算符的最佳实践
- 常见错误和陷阱
分支语句和逻辑运算符是C++程序控制流程的重要组成部分,它们使程序能够根据不同的条件执行不同的代码路径。掌握好这些内容,对于编写结构清晰、逻辑正确的程序至关重要。
在使用分支语句和逻辑运算符时,应注意代码的可读性和可维护性,避免深度嵌套和复杂的条件表达式。同时,要注意常见的错误和陷阱,如赋值运算符与相等运算符混淆、逻辑运算符与位运算符混淆、连续比较错误等。
在后续章节中,我们将学习循环语句、数组、指针等更高级的C++特性,这些特性将与分支语句和逻辑运算符结合使用,帮助我们构建更复杂、更强大的程序。



