第5章 分支语句和逻辑运算符

分支语句

if 语句的高级用法

复合条件

1
2
3
4
5
6
7
8
9
// 使用逻辑运算符组合条件
if (age >= 18 && age <= 65) {
std::cout << "You are eligible to work" << std::endl;
}

// 使用括号提高可读性
if ((temperature > 0 && temperature < 100) || isWater) {
std::cout << "Water is in liquid state" << std::endl;
}

嵌套 if 语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 嵌套if语句
if (score >= 60) {
if (score >= 90) {
std::cout << "Grade: A" << std::endl;
} else if (score >= 80) {
std::cout << "Grade: B" << std::endl;
} else if (score >= 70) {
std::cout << "Grade: C" << std::endl;
} else {
std::cout << "Grade: D" << std::endl;
}
} else {
std::cout << "Grade: F" << std::endl;
}

switch 语句的高级用法

枚举类型与 switch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 枚举类型与switch
enum class Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
};

void printDayActivity(Day day) {
switch (day) {
case Day::MONDAY:
case Day::TUESDAY:
case Day::WEDNESDAY:
case Day::THURSDAY:
case Day::FRIDAY:
std::cout << "Go to work" << std::endl;
break;
case Day::SATURDAY:
case Day::SUNDAY:
std::cout << "Weekend - relax" << std::endl;
break;
default:
std::cout << "Invalid day" << std::endl;
break;
}
}

switch 语句中的变量定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// switch语句中的变量定义
switch (choice) {
case 1: {
int value = 100;
std::cout << "Choice 1: " << value << std::endl;
break;
}
case 2: {
int value = 200;
std::cout << "Choice 2: " << value << std::endl;
break;
}
default:
std::cout << "Invalid choice" << std::endl;
break;
}

逻辑运算符

逻辑运算符的优先级

运算符优先级结合性
!右到左
&&左到右
||左到右

短路求值

逻辑运算符具有短路求值的特性,即当表达式的结果已经确定时,不再计算剩余的部分:

1
2
3
4
5
6
7
8
9
10
11
12
// 短路求值示例
int x = 5;

// 如果x > 10为假,不会计算x / 0,避免除零错误
if (x > 10 && (x / 0) == 0) {
// 不会执行到这里
}

// 如果x < 10为真,不会计算x / 0,避免除零错误
if (x < 10 || (x / 0) == 0) {
std::cout << "This will be executed" << std::endl;
}

短路求值的应用

  1. 安全的空指针检查

    1
    2
    3
    4
    // 安全地访问指针成员
    if (ptr != nullptr && ptr->value > 0) {
    // 只有当ptr不为空时才访问ptr->value
    }
  2. 条件函数调用

    1
    2
    3
    4
    // 只有当条件满足时才调用函数
    if (needUpdate && updateData()) {
    // 执行更新后的操作
    }
  3. 层级检查

    1
    2
    3
    4
    // 层级检查,确保每个环节都有效
    if (user != nullptr && user->profile != nullptr && user->profile->address != nullptr) {
    // 访问user->profile->address
    }
  4. 性能优化

    1
    2
    3
    4
    // 先检查快速条件,再检查慢速条件
    if (simpleCheck() && expensiveCheck()) {
    // 只有当simpleCheck()为真时才执行expensiveCheck()
    }

逻辑表达式的返回值

逻辑表达式返回布尔值,但在C++中,非布尔值也可以用于逻辑表达式:

1
2
3
4
5
6
7
8
9
10
11
bool result1 = (5 > 3 && 2 < 4); // true
bool result2 = (5 > 3 || 2 > 4); // true
bool result3 = !(5 > 3); // false

// 非布尔值的逻辑表达式
int a = 5; // 非零值为true
int b = 0; // 零值为false

bool result4 = (a && b); // false,因为b为0
bool result5 = (a || b); // true,因为a非零
bool result6 = !a; // false,因为a非零

非布尔值的逻辑表达式

  1. 整数类型:零值为false,非零值为true
  2. 指针类型:空指针为false,非空指针为true
  3. 浮点类型:零值为false,非零值为true
  4. 容器类型:空容器的布尔转换通常为false,非空容器为true(C++20+)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 指针类型的逻辑表达式
int* ptr = nullptr;
if (ptr) {
std::cout << "ptr is not null" << std::endl;
}

// 容器类型的逻辑表达式(C++20+)
std::vector<int> v;
if (v) {
std::cout << "v is not empty" << std::endl;
}

// 智能指针的逻辑表达式
std::unique_ptr<int> uptr;
if (uptr) {
std::cout << "uptr is not null" << std::endl;
}

逻辑表达式的性能优化

  1. 条件顺序优化

    • 将快速计算的条件放在前面
    • 将可能性大的条件放在前面
    • 将副作用小的条件放在前面
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 优化前
    if (expensiveCheck() && commonCondition()) {
    // ...
    }

    // 优化后
    if (commonCondition() && expensiveCheck()) {
    // ...
    }
  2. 逻辑表达式的简化

    • 使用德摩根定律简化逻辑表达式
    • 提取重复的条件到变量
    • 使用布尔代数简化复杂表达式
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 德摩根定律示例
    // 原表达式
    if (!(a && b)) {
    // ...
    }

    // 简化后
    if (!a || !b) {
    // ...
    }
  3. 避免不必要的计算

    • 使用短路求值避免不必要的计算
    • 缓存计算结果
    • 避免在逻辑表达式中执行有副作用的操作
    1
    2
    3
    4
    5
    // 避免重复计算
    bool isValid = checkValidity();
    if (isValid && processValidData()) {
    // ...
    }

现代C++中的逻辑表达式

  1. nullptr的使用

    1
    2
    3
    4
    // 现代C++推荐使用nullptr
    if (ptr != nullptr) {
    // ...
    }
  2. std::optional的使用

    1
    2
    3
    4
    5
    // 使用std::optional进行空值检查
    std::optional<int> value = getValue();
    if (value) {
    std::cout << "Value: " << *value << std::endl;
    }
  3. 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;
    }
  4. 概念的使用(C++20+)

    1
    2
    3
    4
    5
    6
    // 使用概念进行编译时类型检查
    template <typename T>
    requires std::integral<T>
    void processIntegral(T value) {
    // ...
    }

关系运算符

关系运算符的优先级

运算符优先级结合性
<, <=, >, >=中高左到右
==, !=左到右

关系表达式的返回值

关系表达式返回布尔值:

1
2
3
4
5
6
7
8
9
10
11
bool result1 = (5 > 3); // true
bool result2 = (5 == 3); // false
bool result3 = (5 != 3); // true

// 连续比较
int a = 1, b = 2, c = 3;
bool result4 = (a < b < c); // 注意:这不是数学中的连续比较
// 实际计算:(a < b) < c → true < c → 1 < 3 → true

// 正确的连续比较
bool result5 = (a < b && b < c); // true

关系运算符的安全性

  1. 整数比较

    • 有符号整数与无符号整数比较时要小心
    • 避免整数溢出导致的比较错误
    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; // 会执行这里
    }
  2. 浮点数比较

    • 避免直接使用==和!=比较浮点数
    • 使用容差进行比较
    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;
    }
  3. 指针比较

    • 只有指向同一数组或对象的指针才能安全比较
    • 空指针可以与任何指针比较
    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++中的比较运算符

  1. 三路比较运算符(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;
    }
  2. 默认比较(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;
    }
  3. 太空船运算符的应用

    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. 使用命名常量

    1
    2
    3
    4
    5
    6
    7
    // 使用命名常量提高可读性
    const int MIN_AGE = 18;
    const int MAX_AGE = 65;

    if (age >= MIN_AGE && age <= MAX_AGE) {
    // ...
    }
  2. 提取复杂条件

    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)) {
    // ...
    }
  3. 使用枚举提高可读性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 使用枚举提高可读性
    enum class Status {
    Inactive,
    Active,
    Suspended
    };

    if (userStatus == Status::Active) {
    // ...
    }
  4. 避免魔法数字

    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
2
3
4
5
6
7
// 嵌套条件运算符
int a = 10, b = 20, c = 30;
int max = (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c);
std::cout << "Max: " << max << std::endl;

// 可读性更好的写法
int maxValue = std::max({a, b, c});

条件运算符与表达式

1
2
3
4
5
6
// 条件运算符用于表达式
std::string result = (score >= 60) ? "Pass" : "Fail";
std::cout << "Result: " << result << std::endl;

// 条件运算符用于函数参数
printGrade((score >= 90) ? 'A' : (score >= 80) ? 'B' : (score >= 70) ? 'C' : (score >= 60) ? 'D' : 'F');

逻辑运算符的应用

1. 范围检查

1
2
3
4
5
6
7
8
9
// 检查值是否在范围内
bool isInRange(int value, int min, int max) {
return (value >= min && value <= max);
}

// 使用示例
if (isInRange(age, 18, 65)) {
std::cout << "Age is in valid range" << std::endl;
}

2. 多重条件检查

1
2
3
4
5
6
7
8
9
10
11
// 检查用户名和密码
bool isValidUser(const std::string& username, const std::string& password) {
return (!username.empty() && !password.empty() && password.length() >= 8);
}

// 使用示例
if (isValidUser(user, pass)) {
std::cout << "Login successful" << std::endl;
} else {
std::cout << "Invalid username or password" << std::endl;
}

3. 状态检查

1
2
3
4
5
6
7
8
9
10
11
// 检查系统状态
bool isSystemReady(bool networkUp, bool databaseUp, bool servicesRunning) {
return (networkUp && databaseUp && servicesRunning);
}

// 使用示例
if (isSystemReady(networkStatus, dbStatus, serviceStatus)) {
std::cout << "System is ready" << std::endl;
} else {
std::cout << "System is not ready" << std::endl;
}

分支语句的最佳实践

1. 代码可读性

  • 缩进:使用一致的缩进风格
  • 空格:在运算符两侧、逗号后添加空格
  • 括号:即使只有一条语句,也使用大括号包围
  • 注释:为复杂的条件添加注释

2. 避免深度嵌套

  • 提取函数:将复杂的嵌套代码提取为单独的函数
  • 使用早期返回:对于简单的错误情况,使用早期返回
  • 简化条件:将复杂的条件表达式分解为多个简单的条件

3. 条件表达式的简化

  • 使用常量:将魔法数字替换为命名常量
  • 使用枚举:将整数常量替换为枚举类型
  • 使用布尔变量:将复杂的条件表达式赋值给有意义的布尔变量

4. switch 语句的最佳实践

  • 包含default分支:处理所有未明确处理的情况
  • 使用break语句:避免意外的穿透行为
  • 保持case标签简洁:每个case标签只处理一个逻辑情况
  • 考虑使用枚举:与枚举类型一起使用,提高类型安全性

常见错误和陷阱

1. 赋值运算符与相等运算符混淆

1
2
3
4
5
6
7
8
9
// 错误:使用赋值运算符而不是相等运算符
if (x = 5) { // 总是为真,因为x被赋值为5
std::cout << "x is 5" << std::endl;
}

// 正确:使用相等运算符
if (x == 5) {
std::cout << "x is 5" << std::endl;
}

2. 逻辑运算符与位运算符混淆

1
2
3
4
5
6
7
8
9
// 错误:使用位运算符而不是逻辑运算符
if (a & b) { // 位与,不是逻辑与
std::cout << "Both a and b are non-zero" << std::endl;
}

// 正确:使用逻辑运算符
if (a && b) {
std::cout << "Both a and b are true" << std::endl;
}

3. 连续比较错误

1
2
3
4
5
6
7
8
9
// 错误:连续比较
if (10 < x < 20) { // 实际计算:(10 < x) < 20
std::cout << "x is between 10 and 20" << std::endl;
}

// 正确:使用逻辑与
if (10 < x && x < 20) {
std::cout << "x is between 10 and 20" << std::endl;
}

4. 浮点数比较错误

1
2
3
4
5
6
7
8
9
10
// 错误:直接比较浮点数
if (x == 0.1) { // 浮点数有精度问题
std::cout << "x is exactly 0.1" << std::endl;
}

// 正确:使用容差比较
const double EPSILON = 1e-9;
if (std::abs(x - 0.1) < EPSILON) {
std::cout << "x is approximately 0.1" << std::endl;
}

5. 短路求值依赖

1
2
3
4
5
6
7
8
9
10
// 错误:依赖短路求值的副作用
int i = 0;
if (i < 5 && (i++) < 10) {
// i会递增
}

// 如果修改为||,行为会改变
if (i < 5 || (i++) < 10) {
// i不会递增,因为i < 5为真
}

小结

本章介绍了C++中的分支语句和逻辑运算符,包括:

  1. 分支语句:if语句、switch语句的高级用法
  2. 逻辑运算符:&&、||、!的用法和短路求值特性
  3. 关系运算符:<、<=、>、>=、==、!=的用法
  4. 条件运算符:?:的嵌套使用
  5. 分支语句和逻辑运算符的最佳实践
  6. 常见错误和陷阱

分支语句和逻辑运算符是C++程序控制流程的重要组成部分,它们使程序能够根据不同的条件执行不同的代码路径。掌握好这些内容,对于编写结构清晰、逻辑正确的程序至关重要。

在使用分支语句和逻辑运算符时,应注意代码的可读性和可维护性,避免深度嵌套和复杂的条件表达式。同时,要注意常见的错误和陷阱,如赋值运算符与相等运算符混淆、逻辑运算符与位运算符混淆、连续比较错误等。

在后续章节中,我们将学习循环语句、数组、指针等更高级的C++特性,这些特性将与分支语句和逻辑运算符结合使用,帮助我们构建更复杂、更强大的程序。