首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【C++篇】 异常处理

【C++篇】 异常处理

作者头像
用户11719958
发布2025-12-30 15:01:32
发布2025-12-30 15:01:32
2340
举报

1,异常的概念及使用

1.1,异常的概念

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

1.2,异常的抛出和捕获

  • 程序出现问题时,我们通过抛出(throw)一个对象来引发一个异常,该对象的类型和当前的调用链决定了应该由哪个catch来捕获处理该异常。
  • 当throw执行时,throw后面的代码不再执行。程序的执行从throw位置跳到与之匹配的catch模块。catch可能是同一个函数中的一个局部的catch,也可能是调用链中另一个函数中的catch。一旦程序开始异常处理,沿着调用链的对象都将被销毁。
  • 抛出异常对象后,会生成一个异常对象的拷贝,因为抛出的异常对象可能是一个局部对象,所以会生成一个拷贝对象,拷贝对象再catch子句后销毁。

代码示例:

#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; } // 输出:错误: 除数不能为零!

1.3,栈展开

  • 抛出 异常后,程序暂停当前函数的执行,开始寻找与之匹配的catch子句,首先检查throw是否再try块内部,如果在则查找匹配的catch语句,如果由匹配的,则跳到catch地方进行处理。
  • 如果当前函数没有try/catch子句,或者有但是类型不匹配,退出当前函数,继续在外层调用函数链中查找,上述查找catch的过程称为栈展开。
  • 如果到达main函数,依旧没有找到匹配的catch子句,则程序会调用terminate函数终止程序。有时候达到main函数,还没有找到匹配的,而我们是不希望终止程序的,所以main函数最后一般都使用catch(...),它可以捕获任意类型的异常,但不知道异常错误是什么
  • 如果找到匹配的catch子句处理后,catch子句代码会继续执行。

1.4,异常的重新抛出

有时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); }

1.5,异常安全问题

  • 异常抛出后,后面的代码就不在执行了,前面申请了资源(内存),后面进行释放。但是中间可能会抛异常,导致资源没有释放,这里就由于异常引发了资源泄露,产生安全性的问题。这里需要使用智能指针的RAII方式解决。
  • 还有析构函数中,如果抛出异常也要谨慎处理,比如要释放 10个资源,释放到第5个时抛出异常,导致后面的资源没有释放,导致资源泄露了。

1.6,异常规范

C++11中 ,函数的参数列表后加一个noexcept表示该函数不会抛异常。但如果一个声明了noexcept的函数抛出了异常,程序会调用terminate终止程序。

noexcept还可以作为运算符去检测一个表达式是否会抛异常。

  • noexcept(expression):根据式表达的结果来决定是否可能抛出异常。如果表达式为 true,则表示不会抛出异常;如果为 false,则表示可能抛出异常。

2,标准库中的异常

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)

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-02-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1,异常的概念及使用
    • 1.1,异常的概念
    • 1.2,异常的抛出和捕获
    • 1.3,栈展开
    • 1.4,异常的重新抛出
    • 1.5,异常安全问题
    • 1.6,异常规范
  • 2,标准库中的异常
  • 小结:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档