MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

C++ 控制结构深入解析与实践指南

2024-10-117.0k 阅读

C++ 控制结构概述

在 C++ 编程中,控制结构起着至关重要的作用。它们允许程序员根据不同的条件来控制程序的执行流程,使得程序不仅仅是顺序执行的指令集合,而是能根据具体情况做出不同响应的灵活系统。C++ 主要包含三种类型的控制结构:顺序结构、选择结构和循环结构。

顺序结构是程序中最基本的执行方式,代码按照编写的先后顺序依次执行,就像我们日常按步骤完成一件事情一样,例如:

#include <iostream>
int main() {
    std::cout << "第一步" << std::endl;
    std::cout << "第二步" << std::endl;
    std::cout << "第三步" << std::endl;
    return 0;
}

在这个简单的示例中,程序会依次输出“第一步”“第二步”“第三步”。

然而,实际的编程场景往往需要程序根据不同条件做出不同的决策,这就引出了选择结构和循环结构。选择结构让程序能够根据条件判断结果选择执行不同的代码块,循环结构则允许重复执行一段代码,直到满足特定条件为止。

选择结构

if - else 语句

  1. 基本形式 if - else 语句是 C++ 中最常用的选择结构。它的基本形式为:
if (condition) {
    // 如果 condition 为真,执行这里的代码
} else {
    // 如果 condition 为假,执行这里的代码
}

例如,我们要判断一个数是否为正数:

#include <iostream>
int main() {
    int num = 10;
    if (num > 0) {
        std::cout << num << " 是正数" << std::endl;
    } else {
        std::cout << num << " 不是正数" << std::endl;
    }
    return 0;
}

在这个例子中,num > 0 就是条件 condition,当 num 的值大于 0 时,会输出“10 是正数”,否则输出“10 不是正数”。

  1. 嵌套 if - else if - else 语句可以嵌套使用,用于处理更复杂的条件判断。例如,我们要判断一个数是否在某个区间内:
#include <iostream>
int main() {
    int num = 15;
    if (num >= 10) {
        if (num <= 20) {
            std::cout << num << " 在 10 到 20 之间" << std::endl;
        } else {
            std::cout << num << " 大于 20" << std::endl;
        }
    } else {
        std::cout << num << " 小于 10" << std::endl;
    }
    return 0;
}

这里外层 if 判断 num 是否大于等于 10,内层 if 在满足外层条件的基础上进一步判断 num 是否小于等于 20。

  1. if - else if - else 链 当有多个互斥条件需要判断时,可以使用 if - else if - else 链:
#include <iostream>
int main() {
    int score = 85;
    if (score >= 90) {
        std::cout << "A 等级" << std::endl;
    } else if (score >= 80) {
        std::cout << "B 等级" << std::endl;
    } else if (score >= 70) {
        std::cout << "C 等级" << std::endl;
    } else {
        std::cout << "其他等级" << std::endl;
    }
    return 0;
}

这个例子根据 score 的值来判断对应的等级,依次检查每个条件,一旦某个条件满足,就执行相应的代码块,并且不会再检查后续的条件。

switch - case 语句

  1. 基本形式 switch - case 语句也是一种选择结构,它根据一个整型或枚举类型的表达式的值来选择执行不同的分支。其基本形式为:
switch (expression) {
    case value1:
        // 当 expression 的值等于 value1 时执行这里的代码
        break;
    case value2:
        // 当 expression 的值等于 value2 时执行这里的代码
        break;
    default:
        // 当 expression 的值与所有 case 的值都不匹配时执行这里的代码
}

例如,根据一个整数来输出对应的星期几:

#include <iostream>
int main() {
    int day = 3;
    switch (day) {
        case 1:
            std::cout << "星期一" << std::endl;
            break;
        case 2:
            std::cout << "星期二" << std::endl;
            break;
        case 3:
            std::cout << "星期三" << std::endl;
            break;
        case 4:
            std::cout << "星期四" << std::endl;
            break;
        case 5:
            std::cout << "星期五" << std::endl;
            break;
        case 6:
            std::cout << "星期六" << std::endl;
            break;
        case 7:
            std::cout << "星期日" << std::endl;
            break;
        default:
            std::cout << "无效的输入" << std::endl;
    }
    return 0;
}

在这个例子中,switch 后的表达式 day 的值决定了执行哪个 case 分支。break 语句用于跳出 switch 语句,防止继续执行下一个 case 分支。

  1. 注意事项
  • case 后的常量表达式必须是整型或枚举类型,且值必须唯一。
  • 如果没有 break 语句,程序会从匹配的 case 开始,依次执行后面所有 case 分支的代码,直到遇到 break 或者 switch 结束。例如:
#include <iostream>
int main() {
    int num = 2;
    switch (num) {
        case 1:
            std::cout << "一" << std::endl;
        case 2:
            std::cout << "二" << std::endl;
        case 3:
            std::cout << "三" << std::endl;
        default:
            std::cout << "其他" << std::endl;
    }
    return 0;
}

在这个例子中,由于 num 为 2,匹配 case 2,但因为没有 break,会继续执行 case 3default 的代码,最终输出“二”“三”“其他”。

循环结构

while 循环

  1. 基本形式 while 循环会在给定条件为真时,重复执行一段代码。其基本形式为:
while (condition) {
    // 当 condition 为真时,执行这里的代码
}

例如,我们要计算 1 到 10 的累加和:

#include <iostream>
int main() {
    int sum = 0;
    int i = 1;
    while (i <= 10) {
        sum += i;
        i++;
    }
    std::cout << "1 到 10 的累加和为:" << sum << std::endl;
    return 0;
}

在这个例子中,while 循环的条件是 i <= 10,只要 i 小于等于 10,就会执行循环体中的代码,每次循环 i 自增 1,直到 i 大于 10 时循环结束。

  1. 无限循环 如果 while 循环的条件始终为真,就会形成无限循环。例如:
#include <iostream>
int main() {
    while (true) {
        std::cout << "这是一个无限循环" << std::endl;
    }
    return 0;
}

这样的无限循环在实际编程中需要谨慎使用,通常需要在循环体内部添加某种条件来跳出循环,比如使用 break 语句。

do - while 循环

  1. 基本形式 do - while 循环与 while 循环类似,但它会先执行一次循环体,然后再检查条件。其基本形式为:
do {
    // 先执行这里的代码
} while (condition);

例如,我们要实现一个简单的猜数字游戏,玩家输入猜测的数字,直到猜对为止:

#include <iostream>
#include <cstdlib>
#include <ctime>
int main() {
    srand(time(0));
    int target = rand() % 100 + 1;
    int guess;
    do {
        std::cout << "请猜一个 1 到 100 之间的数字:";
        std::cin >> guess;
        if (guess > target) {
            std::cout << "猜大了" << std::endl;
        } else if (guess < target) {
            std::cout << "猜小了" << std::endl;
        }
    } while (guess != target);
    std::cout << "恭喜你,猜对了!" << std::endl;
    return 0;
}

在这个例子中,无论玩家第一次猜测的数字是否正确,都会先执行一次循环体,让玩家输入猜测值并进行判断,然后再根据条件决定是否继续循环。

  1. 与 while 循环的区别 while 循环是先判断条件再执行循环体,而 do - while 循环是先执行循环体再判断条件。这意味着 do - while 循环至少会执行一次循环体,而 while 循环如果条件一开始就不满足,循环体一次都不会执行。

for 循环

  1. 基本形式 for 循环是一种功能强大且灵活的循环结构,常用于已知循环次数的情况。其基本形式为:
for (initialization; condition; increment) {
    // 当 condition 为真时,执行这里的代码
}

例如,计算 1 到 100 的偶数和:

#include <iostream>
int main() {
    int sum = 0;
    for (int i = 2; i <= 100; i += 2) {
        sum += i;
    }
    std::cout << "1 到 100 的偶数和为:" << sum << std::endl;
    return 0;
}

在这个 for 循环中,initialization 部分 int i = 2 初始化循环变量 i 为 2;condition 部分 i <= 100 是循环条件,只要 i 小于等于 100 就继续循环;increment 部分 i += 2 每次循环结束后将 i 增加 2。

  1. 省略部分内容 for 循环的三个部分(initializationconditionincrement)都可以省略,但分号不能省略。例如:
  • 省略 initialization
int i = 1;
for (; i <= 5; i++) {
    std::cout << i << " ";
}

这里在 for 循环外部已经初始化了 i,所以 for 循环中省略了 initialization 部分。

  • 省略 condition
for (int i = 1; ; i++) {
    std::cout << i << " ";
    if (i == 5) {
        break;
    }
}

省略 condition 相当于条件永远为真,形成无限循环,这里通过 break 语句在 i 等于 5 时跳出循环。

  • 省略 increment
for (int i = 1; i <= 5;) {
    std::cout << i << " ";
    i++;
}

这里把 increment 的操作放到了循环体内部。

循环控制语句

break 语句

break 语句用于立即终止当前循环(whiledo - whilefor)或 switch - case 语句,跳出其所在的代码块。例如,在一个寻找特定数字的循环中:

#include <iostream>
int main() {
    int numbers[] = {10, 20, 30, 40, 50};
    int target = 30;
    for (int i = 0; i < 5; i++) {
        if (numbers[i] == target) {
            std::cout << "找到了目标数字 " << target << ",索引为 " << i << std::endl;
            break;
        }
    }
    return 0;
}

当在数组中找到目标数字 30 时,break 语句会立即终止 for 循环,不再继续查找后续元素。

continue 语句

continue 语句用于跳过当前循环体中剩余的代码,直接进入下一次循环。例如,我们要输出 1 到 10 中的奇数:

#include <iostream>
int main() {
    for (int i = 1; i <= 10; i++) {
        if (i % 2 == 0) {
            continue;
        }
        std::cout << i << " ";
    }
    return 0;
}

i 是偶数时,continue 语句会跳过 std::cout << i << " "; 这行代码,直接进入下一次循环,从而只输出奇数。

goto 语句

goto 语句可以无条件地跳转到程序中指定的标签处。虽然 goto 语句在现代编程中不被提倡使用,因为它可能会使程序的逻辑变得混乱,但在某些特定情况下仍有其用途。例如:

#include <iostream>
int main() {
    int i = 0;
start:
    std::cout << i << " ";
    i++;
    if (i < 5) {
        goto start;
    }
    return 0;
}

这里 goto start; 语句会跳转到 start: 标签处,形成一个简单的循环。然而,过度使用 goto 可能导致代码难以理解和维护,应尽量避免在复杂程序中使用。

控制结构的嵌套与组合

在实际编程中,控制结构常常会嵌套使用或组合使用,以实现复杂的逻辑。例如,我们可以用嵌套的 for 循环来打印一个乘法表:

#include <iostream>
int main() {
    for (int i = 1; i <= 9; i++) {
        for (int j = 1; j <= i; j++) {
            std::cout << j << "×" << i << "=" << i * j << "\t";
        }
        std::cout << std::endl;
    }
    return 0;
}

这里外层 for 循环控制行数,内层 for 循环控制每行的乘法表达式数量,通过嵌套实现了乘法表的打印。

又如,我们可以结合 if - else 和循环结构来处理更复杂的业务逻辑。比如,在一个学生成绩管理系统中,我们要统计不同等级的学生人数:

#include <iostream>
int main() {
    int scores[] = {85, 92, 78, 65, 98};
    int countA = 0, countB = 0, countC = 0;
    for (int i = 0; i < 5; i++) {
        if (scores[i] >= 90) {
            countA++;
        } else if (scores[i] >= 80) {
            countB++;
        } else {
            countC++;
        }
    }
    std::cout << "A 等级人数:" << countA << std::endl;
    std::cout << "B 等级人数:" << countB << std::endl;
    std::cout << "C 等级人数:" << countC << std::endl;
    return 0;
}

这里通过 for 循环遍历学生成绩数组,再结合 if - else 语句对每个成绩进行等级判断,并统计各等级的人数。

控制结构在面向对象编程中的应用

在 C++ 的面向对象编程中,控制结构同样起着重要作用。例如,在类的成员函数中,我们可以使用控制结构来实现不同的业务逻辑。假设我们有一个 Rectangle 类,用于表示矩形,有计算面积和判断是否为正方形的功能:

#include <iostream>
class Rectangle {
private:
    int width;
    int height;
public:
    Rectangle(int w, int h) : width(w), height(h) {}
    int getArea() {
        return width * height;
    }
    bool isSquare() {
        if (width == height) {
            return true;
        } else {
            return false;
        }
    }
};
int main() {
    Rectangle rect1(5, 10);
    Rectangle rect2(5, 5);
    std::cout << "rect1 的面积:" << rect1.getArea() << std::endl;
    if (rect1.isSquare()) {
        std::cout << "rect1 是正方形" << std::endl;
    } else {
        std::cout << "rect1 不是正方形" << std::endl;
    }
    std::cout << "rect2 的面积:" << rect2.getArea() << std::endl;
    if (rect2.isSquare()) {
        std::cout << "rect2 是正方形" << std::endl;
    } else {
        std::cout << "rect2 不是正方形" << std::endl;
    }
    return 0;
}

isSquare 成员函数中,使用了 if - else 语句来判断矩形是否为正方形。在 main 函数中,通过 if - else 语句根据 isSquare 的返回值输出相应信息。

再比如,在处理对象数组时,我们可以使用循环结构。假设我们有一个 Student 类,并且有一个学生数组,我们要统计所有学生的平均成绩:

#include <iostream>
class Student {
private:
    std::string name;
    int score;
public:
    Student(const std::string& n, int s) : name(n), score(s) {}
    int getScore() {
        return score;
    }
};
int main() {
    Student students[] = {Student("Alice", 85), Student("Bob", 90), Student("Charlie", 78)};
    int totalScore = 0;
    for (int i = 0; i < 3; i++) {
        totalScore += students[i].getScore();
    }
    double averageScore = static_cast<double>(totalScore) / 3;
    std::cout << "学生的平均成绩为:" << averageScore << std::endl;
    return 0;
}

这里通过 for 循环遍历 Student 对象数组,获取每个学生的成绩并累加,最后计算平均成绩。

控制结构与异常处理

在 C++ 中,异常处理也是程序控制流程的一部分。异常处理机制允许程序在遇到错误或异常情况时,改变正常的执行流程,跳转到专门的异常处理代码块。例如,我们在进行除法运算时,可能会遇到除数为 0 的情况,这时可以抛出异常并进行处理:

#include <iostream>
double divide(double a, double b) {
    if (b == 0) {
        throw "除数不能为 0";
    }
    return a / b;
}
int main() {
    try {
        double result = divide(10, 2);
        std::cout << "10 / 2 的结果为:" << result << std::endl;
        result = divide(5, 0);
        std::cout << "5 / 0 的结果为:" << result << std::endl;
    } catch (const char* msg) {
        std::cout << "捕获到异常:" << msg << std::endl;
    }
    return 0;
}

divide 函数中,如果除数 b 为 0,就抛出一个字符串类型的异常。在 main 函数中,使用 try - catch 块来捕获并处理异常。当 divide(5, 0) 执行时,会抛出异常,程序跳转到 catch 块中执行,输出“捕获到异常:除数不能为 0”,而不会继续执行 std::cout << "5 / 0 的结果为:" << result << std::endl; 这行代码。

异常处理与控制结构相互配合,可以让程序在面对各种意外情况时,保持良好的健壮性和稳定性,确保程序能够按照预期的方式运行,避免因为错误而导致程序崩溃。

通过深入理解和灵活运用 C++ 的各种控制结构,程序员能够编写出逻辑清晰、功能强大且高效的程序,无论是简单的控制台应用,还是复杂的大型项目,控制结构都是实现程序业务逻辑的核心工具之一。在实际编程过程中,需要根据具体的需求和场景,合理选择和组合控制结构,以达到最佳的编程效果。同时,随着编程经验的积累,对控制结构的运用也会更加得心应手,能够编写出更加优雅和高效的代码。