异常处理是C++用于管理 程序运行时错误的核心机制,通过try,catch,throw等关键字实现。它允许将错误检测与处理逻辑分离,提升代码的可读性和健壮性。

代码示例:
#include <iostream> #include <stdexcept> double divide(int a, int b) { if (b == 0) { throw std::runtime_error("除数不能为零!"); //抛出异常对象 } return a / b; } int main() { try { std::cout << divide(10, 0) << std::endl; } catch (const std::runtime_error& e) { //捕获异常,处理异常 std::cerr << "错误: " << e.what() << std::endl; } return 0; } // 输出:错误: 除数不能为零!

有时catch到一个异常对象 后,需要对错误进行分类,其中的某种异常错误 需要进行特殊处理,其他异常则重新抛出给外层调用链处理。捕获异常后重新抛出,直接throw,就可以把捕获的对象重新抛出。
常见应用场景:
1,中间层处理部分逻辑
在多层调用中,中间层捕获异常后记录日志或释放资源,但不处理异常的根本原因:
void middleLayer() { try { someRiskyOperation(); } catch (const std::exception& e) { logError(e.what()); // 记录日志 throw; // 重新抛出,让上层处理 } }
2,包装异常
捕获原始异常后,抛出一个新的异常类型(如自定义异常),同时保留原始异常信息:
try { // ... } catch (const DatabaseException& e) { throw MyAppException("Database error: " + std::string(e.what())); }
3,跨线程异常传递
使用 std::exception_ptr 保存异常,稍后在另一线程中重新抛出:
std::exception_ptr eptr; try { // 可能抛出异常的代码 } catch (...) { eptr = std::current_exception(); // 捕获并保存异常 } // 在另一个线程中重新抛出 if (eptr) { std::rethrow_exception(eptr); }
C++11中 ,函数的参数列表后加一个noexcept表示该函数不会抛异常。但如果一个声明了noexcept的函数抛出了异常,程序会调用terminate终止程序。
noexcept还可以作为运算符去检测一个表达式是否会抛异常。
noexcept(expression):根据式表达的结果来决定是否可能抛出异常。如果表达式为 true,则表示不会抛出异常;如果为 false,则表示可能抛出异常。

C++标准库定义了一套自己的异常体系,基类是exception,所以我们在捕获异常时,捕获exception即可,要获取异常信息,调用what()函数,what()是一个虚函数,派生类可以重写。
(1)C++标准库中常见的类型(继承自:std::exception)
std::logic_error:程序逻辑错误(如无效参数)。
std::runtime_error:运行时错误(如文件未找到)。
std::bad_alloc:内存分配失败(new失败时抛出)。
(2)自定义异常
通过继承std::exception创建自定义异常类:
class MyException : public std::exception { public: MyException(const char* msg) : message(msg) {} const char* what() const noexcept override { return message.c_str(); } private: std::string message; }; // 使用 throw MyException("自定义异常!");
(1)异常处理的性能影响
抛出异常时:栈展开(Stack Unwinding)和类型匹配会带来一定开销,不适合高频场景。
(2) 最佳实践
1,优先使用RAII(如智能指针):确保资源的自动释放
2,避免在析构函数中抛出异常:可能会导致程序终止(若异常未被捕获)
3,明确异常规格:使用noexcept标记不会抛异常的函数。
4,捕获异常按引用:避免对象切片如:catch (const std::exception& e)。