C++ 异常安全(二):try-catch 语句的注意事项

C++ 的try-catch 语句用于处理异常。当在 try 块中的代码抛出一个异常时,程序会立即跳出 try 块,并查找与之匹配的 catch 块来执行。

以下是使用 try-catch 语句的注意事项:

1. 异常类型匹配

  • catch 块后面必须跟上一个异常类型(或者是省略类型以捕获所有类型的异常)。
  • 当异常被抛出时,会寻找第一个与之类型匹配的 catch 块来执行。
  • 如果存在多个 catch 块,它们将按照它们在代码中出现的顺序进行匹配。
  • 如果没有找到匹配的 catch 块,程序会调用 std::terminate 并结束。

2. 捕获所有异常

  • 可以使用 catch(...) 来捕获所有类型的异常。这通常用于在顶层(如 main 函数)捕获所有未处理的异常,并给出一些通用的错误消息。

3. 异常规格说明

  • 在 C++11 之前,函数可以有一个异常规格,列出该函数可能抛出的所有异常类型。但是,这个功能在 C++11 中被弃用,并在 C++17 中被移除。现在,应使用 noexcept 关键字代替原来的异常规格说明。

4. 避免在析构函数中抛出异常

  • 如果在析构函数中抛出异常,并且该异常没有被捕获,那么程序会调用 std::terminate 并结束。这是因为析构函数通常是在处理另一个异常的过程中被调用的,如果在此时再次抛出异常,那么情况会变得更加复杂且难以处理。
  • 如果在析构函数中需要处理错误,通常的做法是记录错误(例如,写入日志),而不是抛出异常。

5. 避免使用空 catch 块

  • 空 catch 块会捕获所有类型的异常,但并不进行任何处理。这通常不是一个好的做法,因为它会隐藏可能的问题,并使调试变得更加困难。

6. 资源管理

  • 使用 RAII(Resource Acquisition Is Initialization)原则来管理资源。应该使用智能指针(如 std::unique_ptr 或 std::shared_ptr)来自动管理动态分配的内存,或者使用作用域内的对象来管理其他类型的资源(如文件句柄、数据库连接等)。这样,当异常发生时,这些资源会被自动释放,从而减少了内存泄漏和其他问题的风险。

7. 异常传播

  • 如果在函数中捕获了一个异常,并且该异常不应该在该函数中处理,可以使用 throw;(没有参数)来重新抛出该异常。这将使异常传播到调用该函数的代码中。

8. 避免过度使用异常

  • 异常处理是一种强大的工具,但它并不适用于所有情况。对于可以预见并容易处理的错误情况,使用错误代码或返回值可能更为合适。过度使用异常可能会导致代码变得难以理解和维护。

更进一步地,可参见如下详细介绍:

  1. try 关键字的作用域不应为空
  2. catch 子句不应为空
  3. 通过引用捕获异常
  4. 不应嵌套 try-catch 语句
  5. 捕获所有异常的 catch(...) 子句应位于最后
  6. 面向派生类的 catch 子句应排在面向基类的 catch 子句之前
  7. 重新抛出异常时应使用空 throw 表达式(throw;)
  8. 不应在 catch 子句外使用空 throw 表达式(throw;)
  9. 在面向构造或析构函数体的 catch 子句中不可访问非静态成员

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值