Effective C++条款08:别让异常逃离析构函数

本文讨论了析构函数抛出异常的潜在问题及其对程序的影响。在C++中,析构函数可能因调用抛出异常的成员函数而抛出异常,这可能导致程序意外终止。为了解决这个问题,提出了三种策略:一是立即终止程序,二是忽略异常,三是通过状态跟踪避免异常。推荐的做法是通过类的设计来防止析构函数抛出异常,确保资源的正确管理,并提供独立的异常安全操作接口。
摘要由CSDN通过智能技术生成

一、析构函数也会抛出异常

  • C++并不禁止析构函数抛出异常,但是不建议这样

演示案例

 
  1. class Widget

  2. {

  3. public:

  4. ~Widget() {} //假设这个析构函数可能会抛出异常

  5. };

  6.  
  7. int main()

  8. {

  9. std::vector<Widget> v;

  10. return 0;

  11. }//v在这里自动销毁

  • 假设v内有10个Widgets,那么在程序结束时会逐个释放这10个Widget对象
  • 但是假设在释放第1个对象时,第1个Widget的析构函数中抛出了异常,并且没有对任何异常进行任何处理,此时程序就会中断

二、通过例子来告知如何处理异常

  • 现在建立以下两个类,一个类负责连接数据库,另一个用来管理数据库对象
 
  1. class DBConnection

  2. {

  3. public:

  4. //该函数返回一个DBConnection对象

  5. static DBConnection create();

  6.  
  7. //关闭数据库连接(失败会抛出异常)

  8. void close();

  9. };

  10.  
  11. //用来管理DBConnection对象

  12. class DBConn

  13. {

  14. public:

  15. //确保数据库连接总是会被关闭

  16. ~DBConn() { db.close(); }

  17. private:

  18. DBConnection db;

  19. };

  • 如果此时客户写出了这样的代码:
 
  1. int main()

  2. {

  3. //建立一个DBConnection对象并交给DBConn管理,使用DBConn的接口管理DBConnection

  4. DBConn dbc(DBConnection::create());

  5.  
  6. return 0;

  7. }//程序结束时,DBConn对象被销毁,因此会自动为DBConnection对象调用close

  • 如果调用close()调用成功的话那么就没有什么事。如果close()函数调用出错(有异常),那么DBConn析构函数也会传播该异常,导致程序出错

两种普通的解决办法

  • 解决办法①:
    • 如果close函数抛出异常,就结束程序,可以通过调用abort完成
    • 如果程序在析构期间发生一个错误,那么“强迫程序结束”是一个合理的设置
 
  1. DBConn::~DBConn()

  2. {

  3. try { db.close(); }

  4. catch (...) {

  5. //此处还可以做一个记录,记下对close的调用失败

  6. std::abort();

  7. }

  8. }

  • 解决办法②:
    • 忽略(吞掉)这个异常
    • 一般而言,忽略这个异常是个坏主意,因为忽略这个异常会造成不明确的行为
 
  1. DBConn::~DBConn()

  2. {

  3. try { db.close(); }

  4. catch (...) {

  5. /*此处还可以做一个记录,记下对close的调用失败,

  6. 其他什么都不做

  7. */

  8. }

  9. }

一个更好的解决办法

  • 一个更好的策略是重新设计DBConn接口,DBConn可以追踪所管理的DBConnection是否已经关闭
    • 如果已经关闭就不做任何事情
    • 如果还没关闭,并且抛出了异常,那么还是要使用到上面的两种解决方案
 
  1. class DBConn

  2. {

  3. public:

  4. ...

  5. void close()

  6. {

  7. db.close();

  8. closed = true; //为true是为了阻止db.close()异常导致DBConn析构异常

  9. }

  10. DBConn::~DBConn()

  11. {

  12. if (!closed) {   

  13. try { db.close(); }  //关闭连接(如果客户不那么做的话)

  14. catch () {

  15. //此处还可以做一个记录,记下对close的调用失败

  16. }

  17. }

  18. }

  19. private:

  20. DBConnection db;

  21. bool closed;

  22. };

三、总结

  • 析构函数绝对不要抛出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕获并处理该异常
  • 如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么类应该提供一个普通函数(而非在析构函数中)执行该操作
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值