🛑 C++ 异常处理教程(零基础版)

从0开始学习 try-catch-throw,通俗易懂 + 示例代码 + 图解风格


🔍 什么是异常处理?

在程序运行过程中,有时会遇到错误或意外情况,比如:

  • 文件找不到
  • 内存分配失败
  • 数组越界访问
  • 除以0等非法操作

如果不对这些“异常”进行处理,程序可能会崩溃甚至中止。

C++ 的异常处理机制 就是为了让程序可以优雅地应对这些错误,并尽可能恢复或给出提示,而不是直接崩溃。


⚙️ 异常处理的三要素:try, catch, throw

关键字 含义
try 包含可能抛出异常的代码块
throw 主动抛出一个异常
catch 捕获并处理异常

✅ 基本结构:

try {
    // 可能抛出异常的代码
    throw 异常对象;
} catch (异常类型 e) {
    // 处理异常
}

🧠 异常处理的工作流程图解

[ try 中发生异常 ]
        │
        ▼
   [ 抛出异常 throw ]
        │
        ▼
[ catch 捕获并处理异常 ]
        │
        ▼
[ 程序继续执行(不是崩溃)]

💡 示例一:基本异常处理

📌 场景:尝试除以0

#include <iostream>
using namespace std;

int main() {
    try {
        int a = 10, b = 0;
        if (b == 0)
            throw "除数不能为0";  // 抛出异常

        int result = a / b;  // 这里不会执行了
        cout << "结果是:" << result << endl;
    }
    catch (const char* msg) {  // 捕获字符串类型的异常
        cout << "捕获到异常:" << msg << endl;
    }

    cout << "程序继续运行..." << endl;
    return 0;
}

✅ 输出:

捕获到异常:除数不能为0
程序继续运行...

🎯 示例二:捕获多种异常类型

你可以根据抛出的不同类型来分别处理异常。

#include <iostream>
using namespace std;

int main() {
    try {
        int choice;
        cout << "请输入选择(1:整数异常, 2:浮点异常, 3:字符串异常):";
        cin >> choice;

        if (choice == 1)
            throw 100;               // 整型异常
        else if (choice == 2)
            throw 3.14;              // 浮点异常
        else if (choice == 3)
            throw "未知错误";        // 字符串异常
    }
    catch (int e) {
        cout << "捕获到整型异常:" << e << endl;
    }
    catch (double e) {
        cout << "捕获到浮点异常:" << e << endl;
    }
    catch (const char* e) {
        cout << "捕获到字符串异常:" << e << endl;
    }

    cout << "程序继续执行..." << endl;
    return 0;
}

🧩 示例三:自定义异常类(面向对象方式)

我们可以使用类来自定义更复杂的异常。

#include <iostream>
#include <string>
using namespace std;

// 自定义异常类
class MyException {
public:
    string message;
    MyException(string msg) : message(msg) {}
};

int main() {
    try {
        bool error = true;
        if (error)
            throw MyException("这是一个自定义异常!");
    }
    catch (MyException& e) {
        cout << "自定义异常信息:" << e.message << endl;
    }

    cout << "程序正常结束!" << endl;
    return 0;
}

✅ 输出:

自定义异常信息:这是一个自定义异常!
程序正常结束!

🧰 标准库中的常见异常类(需要头文件 <stdexcept>

类名 描述
std::exception 所有标准异常的基类
std::runtime_error 运行时错误,如除以0
std::range_error 结果超出范围
std::overflow_error 上溢出
std::underflow_error 下溢出
std::logic_error 逻辑错误,如无效参数
std::invalid_argument 非法参数错误
std::out_of_range 越界访问

✅ 示例四:使用标准库异常

#include <iostream>
#include <stdexcept>
using namespace std;

double divide(int a, int b) {
    if (b == 0)
        throw runtime_error("除数不能为0");
    return static_cast<double>(a) / b;
}

int main() {
    try {
        double res = divide(10, 0);
    }
    catch (runtime_error& e) {
        cout << "运行时异常:" << e.what() << endl;
    }

    cout << "程序继续执行..." << endl;
    return 0;
}

✅ 输出:

运行时异常:除数不能为0
程序继续执行...

🎨 异常处理流程图(伪代码风格)

开始
│
├─→ try 块开始
│
├─→ 如果没有异常 → 正常执行
│
├─→ 如果有异常 → throw 抛出
│
├─→ catch 块匹配异常类型
│
├─→ 处理异常
│
└─→ 继续执行后续代码

🧪 使用建议 & 最佳实践

建议 说明
🔄 不要滥用异常 只用于真正“异常”的情况,不要用作普通控制流
📦 捕获异常时尽量具体 推荐使用具体的类型(如 int, string, 自定义类),而非 ...
🧹 清理资源 catch 中也要注意释放内存、关闭文件等资源
🧠 使用基类统一处理 如果希望统一处理所有异常,可以用 catch(exception& e)
📤 使用 .what() 方法输出详细信息 对于标准异常非常实用

🧠 异常安全保证级别(高级概念)

级别 说明
No-throw guarantee 函数不会抛出任何异常
Strong guarantee 如果抛出异常,状态回滚到调用前
Basic guarantee 异常抛出后,对象仍处于有效状态
No guarantee 无法保证异常后的状态

🙋‍♂️ 如何继续学习?

如果你希望我继续扩展以下内容,请告诉我:

  • 如何用 RAII 实现异常安全?
  • 如何捕获所有异常?
  • 如何将异常与日志系统结合?
  • 如何实现嵌套异常处理结构?

😊


📌 结语:
通过本教程,你已经掌握了 C++ 异常处理的基本语法和使用场景。它是编写健壮程序的重要组成部分,特别是在大型项目中,合理的异常处理可以大大提升程序的稳定性和可维护性!

如果你还有疑问,或者想要配套练习题,也可以随时告诉我 😊

1 条评论

  • @ 2025-5-19 22:02:44

    C++ 异常处理教程(零基础版)

    C++ 异常处理是一种处理程序运行时错误的机制,它允许程序在遇到错误时能够优雅地处理问题,而不是直接崩溃。以下是针对零基础学习者的异常处理教程。

    1. 什么是异常?

    异常是程序在执行期间发生的错误或意外情况,例如:

    • 除零错误
    • 访问不存在的内存地址
    • 文件打开失败
    • 数组越界访问

    2. 异常处理的基本语法

    C++ 异常处理通过三个关键字实现:trycatchthrow

    try {
        // 可能抛出异常的代码块
        if (出现错误条件) {
            throw 异常值; // 抛出异常
        }
    }
    catch (异常类型 变量名) {
        // 处理异常的代码块
        // 可以使用变量名访问异常信息
    }
    

    3. 抛出异常 (throw)

    使用 throw 语句在程序中抛出异常。异常可以是任何数据类型(通常是类或结构体)。

    示例:除零错误处理

    #include <iostream>
    using namespace std;
    
    double divide(double a, double b) {
        if (b == 0) {
            throw "Division by zero!"; // 抛出字符串类型的异常
        }
        return a / b;
    }
    
    int main() {
        try {
            double result = divide(10.0, 0.0);
            cout << "Result: " << result << endl;
        }
        catch (const char* error) {
            cout << "Error: " << error << endl;
        }
        return 0;
    }
    

    输出:

    Error: Division by zero!
    

    4. 捕获异常 (catch)

    使用 catch 块捕获并处理异常。catch 块必须紧跟在 try 块之后。

    示例:捕获不同类型的异常

    try {
        int age = -5;
        if (age < 0) {
            throw string("Age cannot be negative!"); // 抛出 string 类型异常
        }
    }
    catch (const char* msg) {
        cout << "C-style string exception: " << msg << endl;
    }
    catch (string& msg) {
        cout << "String exception: " << msg << endl; // 捕获 string 类型异常
    }
    

    5. 多重 catch

    可以使用多个 catch 块捕获不同类型的异常,按顺序匹配。

    示例:

    try {
        // 可能抛出多种类型异常的代码
    }
    catch (int errorCode) {
        cout << "Integer error: " << errorCode << endl;
    }
    catch (double errorValue) {
        cout << "Double error: " << errorValue << endl;
    }
    catch (...) { // 捕获所有类型的异常(放最后)
        cout << "Unknown exception occurred." << endl;
    }
    

    6. 标准异常类

    C++ 标准库提供了一组异常类,位于 <exception> 头文件中。

    常见标准异常类:

    • std::exception:所有标准异常的基类
    • std::runtime_error:运行时错误
    • std::logic_error:逻辑错误
    • std::out_of_range:越界访问
    • std::invalid_argument:无效参数

    示例:使用标准异常

    #include <stdexcept> // 包含标准异常头文件
    
    int main() {
        try {
            vector<int> vec(5);
            vec.at(10) = 100; // at() 方法会抛出 out_of_range 异常
        }
        catch (const out_of_range& e) {
            cout << "Exception: " << e.what() << endl;
        }
        return 0;
    }
    

    输出:

    Exception: vector::_M_range_check: __n (which is 10) >= this->size() (which is 5)
    

    7. 自定义异常类

    可以通过继承 std::exception 类来创建自定义异常。

    示例:

    #include <stdexcept>
    #include <string>
    
    class MyException : public std::exception {
    private:
        std::string message;
    public:
        MyException(const std::string& msg) : message(msg) {}
        const char* what() const noexcept override {
            return message.c_str();
        }
    };
    
    // 使用自定义异常
    void checkAge(int age) {
        if (age < 0) {
            throw MyException("Invalid age: Age cannot be negative.");
        }
    }
    

    8. try 块嵌套

    try 块可以嵌套,内层未处理的异常会被外层 catch 捕获。

    示例:

    try {
        try {
            throw 42; // 抛出整数异常
        }
        catch (double e) {
            cout << "Inner catch: " << e << endl;
        }
    }
    catch (int e) {
        cout << "Outer catch: " << e << endl; // 捕获整数异常
    }
    

    9. 异常规范(C++11 前)

    早期 C++ 允许函数声明异常规范,但 C++11 后已弃用,推荐使用 noexcept。

    旧语法(不推荐):

    void func() throw(int, char); // 可能抛出 int 或 char 类型异常
    void safeFunc() throw();      // 不抛出任何异常
    

    现代语法:

    void func() noexcept;         // 承诺不抛出异常
    

    10. 最佳实践

    1. 只捕获能处理的异常:不要盲目捕获所有异常。
    2. 使用具体的异常类型:避免使用通用的 catch(...)
    3. 异常安全性:确保资源释放(如文件关闭、内存释放)。
    4. 自定义异常:为特定错误创建有意义的异常类。
    5. 避免异常泄露:析构函数和 noexcept 函数不应抛出异常。

    总结

    异常处理是 C++ 中处理错误的强大机制,通过 trycatchthrow 可以优雅地处理运行时错误,提高程序的健壮性。建议结合标准异常类和自定义异常类,根据实际需求选择合适的异常处理策略。

    通过练习上述示例,你可以掌握 C++ 异常处理的基础知识。

    • 1