异常处理

C++ 异常处理是通过 trycatchthrow 关键字来实现的,主要用于在程序运行过程中出现错误时进行错误捕获与处理,从而提高程序的健壮性和可维护性。


一、基本语法结构

try {
    // 可能抛出异常的代码
    throw 异常对象;
}
catch (异常类型1 参数名) {
    // 对异常类型1的处理
}
catch (异常类型2 参数名) {
    // 对异常类型2的处理
}
// ...
catch (...) {
    // 捕获所有类型的异常(兜底处理)
}

二、示例

#include <iostream>
using namespace std;

int divide(int a, int b) {
    if (b == 0)
        throw runtime_error("除数不能为0");  // 抛出异常
    return a / b;
}

int main() {
    try {
        int result = divide(10, 0);
        cout << "结果是: " << result << endl;
    }
    catch (const runtime_error& e) {
        cout << "发生异常: " << e.what() << endl;
    }
    catch (...) {
        cout << "发生了未知异常" << endl;
    }
    return 0;
}

输出:

发生异常: 除数不能为0

三、异常类型

  • 基本类型:如 int, const char*(不推荐)
  • STL 标准异常类(推荐):
    • std::exception:所有标准异常的基类
    • std::runtime_error:运行时错误
    • std::logic_error:逻辑错误
    • std::out_of_range, std::invalid_argument 等更具体的派生类

四、自定义异常类

你可以自定义异常类并继承自 std::exception

class MyException : public std::exception {
public:
    const char* what() const noexcept override {
        return "这是一个自定义异常";
    }
};

五、注意事项

  1. 异常是运行时处理机制,不影响编译过程。
  2. 不要滥用异常,在性能敏感的代码中尽量使用错误码。
  3. 析构函数应为 noexcept(否则在异常传播时若析构再抛异常,程序将调用 std::terminate())。
  4. throw; 可以在 catch 中重新抛出当前异常。
  5. 异常处理机制的开销存在,但在正确使用时能显著提高代码可读性与鲁棒性。

六、C++11/14/17 中对异常处理的增强

C++11 和 C++17 对异常处理机制进行了增强和优化,主要包括更严格的异常安全规范、更丰富的异常传递工具,以及性能上的改进。下面逐一介绍重点特性:


1. noexcept 关键字(替代 throw()

用法:

void f() noexcept;         // 保证不抛出异常
void g() noexcept(true);   // 同上
void h() noexcept(false);  // 可能抛出异常(等同于无 noexcept)

编译器行为:

  • 如果函数标记为 noexcept,但在运行中抛出了异常,则程序会调用 std::terminate()
  • 编译器可基于 noexcept 优化函数的调用,例如移动构造(std::vector 仅在元素移动构造是 noexcept 时才优先使用 move)。

示例:

void may_throw();
void safe_func() noexcept {
    // may_throw(); // ❌ 如果调用了可能抛异常的函数,这里编译报错
}

2. noexcept 运算符

可以用来在编译期判断一个表达式是否 noexcept

template <typename T>
void func(T&& x) noexcept(noexcept(T(std::move(x)))) {
    // ...
}

3. std::nested_exception / std::throw_with_nested

用于嵌套异常信息,使异常链条更清晰(类似 Java 的异常链)。

示例:

#include <iostream>
#include <exception>
#include <stdexcept>

void lowLevel() {
    throw std::runtime_error("low-level error");
}

void highLevel() {
    try {
        lowLevel();
    } catch (...) {
        std::throw_with_nested(std::runtime_error("high-level context"));
    }
}

void print_exception(const std::exception& e, int level = 0) {
    std::cerr << std::string(level * 2, ' ') << "Exception: " << e.what() << '\n';
    try {
        std::rethrow_if_nested(e);
    } catch (const std::exception& nested) {
        print_exception(nested, level + 1);
    } catch (...) {}
}

int main() {
    try {
        highLevel();
    } catch (const std::exception& e) {
        print_exception(e);
    }
}

输出:

Exception: high-level context
  Exception: low-level error

4. C++17: std::uncaught_exceptions()

取代 C++98 的 std::uncaught_exception()(注意多了个 s),可以判断当前栈上有多少个未处理的异常。

用于实现 RAII + 异常安全

struct Transaction {
    int old_uncaught = std::uncaught_exceptions();

    ~Transaction() {
        if (std::uncaught_exceptions() > old_uncaught) {
            std::cout << "Rollback!\n";
        } else {
            std::cout << "Commit!\n";
        }
    }
};

⚠ 注意事项

  • noexcept 函数不能抛异常,否则程序终止,建议仅在能保证不抛异常的函数上使用。
  • 异常嵌套机制不影响原有 catch 捕获链,但可以辅助追踪复杂异常因果关系。
  • std::uncaught_exceptions() 是实现事务和资源回滚模式的重要工具。
posted @ 2025-06-20 16:52  aixueforever  阅读(35)  评论(0)    收藏  举报