从语法上来说,析构函数可以抛出异常,但从逻辑上和风险控制上,析构函数中不要抛出异常,因为栈展开容易导致资源泄露和程序崩溃,所以别让异常逃离析构函数。

1.析构函数抛出异常的问题

析构函数从语法上是可以抛出异常的,但是这样做很危险,请尽量不要这要做。原因在《More Effective C++》中提到两个:
(1)如果析构函数抛出异常,则异常点之后的程序不会执行,如果析构函数在异常点之后执行了某些必要的动作比如释放某些资源,则这些动作不会执行,会造成诸如资源泄漏的问题。
(2)通常异常发生时,c++的异常处理机制在异常的传播过程中会进行栈展开(stack-unwinding),因发生异常而逐步退出复合语句和函数定义的过程,被称为栈展开。在栈展开的过程中就会调用已经在栈构造好的对象的析构函数来释放资源,此时若其他析构函数本身也抛出异常,则前一个异常尚未处理,又有新的异常,会造成程序崩溃。

2.解决办法

如果析构函数必须执行一个动作,而该动作可能会在失败时抛出异常,该怎么办?举个例子,假设你使用一个class负责数据库连接:

class DBConnection
{
public:...static DBConnection create(); //返回DBConnection对象;为求简化暂略参数void close(); //关闭联机;失败则抛出异常。
};

为确保客户不忘记在DBConnection对象身上调用close(),一个合理的想法是创建一个用来管理DBConection资源的class,并在其析构函数中调用close。这就是著名的以对象管理资源。

//这个class用来管理DBConnection对象
class DBConn
{
public:...DBConn(const DBConnection& db){this->db=db;}~DBConn() //确保数据库连接总是会被关闭{db.close();}private:DBConnection db;
};

如果调用close成功,没有任何问题。但如果该调用导致异常,DBConn析构函数会传播该异常,如果离开析构函数,那会造成问题,解决办法如下:

2.1 结束程序

如果close抛出异常就结束程序,通常调用abort完成:

DBConn::~DBconn()
{try{db.close(); }catch(...){abort();}
}

如果程序遭遇一个“于析构期间发生的错误”后无法继续执行,“强制结束程序”是个合理选项,毕竟它可以阻止异常从析构函数传播出去导致不明确行为。

2.2 吞下因调用 close 而发生的异常

DBConn::~DBConn
{try{ db.close();}catch(...) {//制作运转记录,记下对close的调用失败!}
}

一般而言,将异常吞掉是个坏主意,因为面对动作失败选择无所作为,然而有时候吞下异常比“草率结束程序”或“不明确行为带来的风险”好。能够这么做的一个前提就是程序必须能够继续可靠的执行。

2.3 重新设计 DBConn 接口,使其客户有机会对可能出现的异常作出反应

我们可以给DBConn添加一个close函数,赋予客户一个机会可以处理“因该操作而发生的异常”。把调用close的责任从DBConn析构函数手上移到DBConn客户手中,你也许会认为它违反了“让接口容易被正确使用”的忠告。实际上这污名并不成立。如果某个操作可能在失败的时候抛出异常,而又存在某种需要必须处理该异常,那么这个异常必须来自析构函数以外的某个函数。因为析构函数吐出异常就是危险,总会带来“过早结束程序”或“发生不明确行为”的风险。

class DBConn
{
public:...void close() //供客户使用的新函数{db.close();closed = true;}~DBConn(){if(!closed){try        //关闭连接(如果客户不调用DBConn::close){       db.close();}catch(...) //如果关闭动作失败,记录下来并结束程序或吞下异常{ 制作运转记录,记下对close的调用失败;...}}}
private:DBConnection db;bool closed;
};

本例要说的是,由客户自己调用close并不会对他们带来负担,而是给他们一个处理错误的机会。如果他们不认为这个机会有用(或许他们坚信不会有错误发生),可能忽略它,依赖DBConn析构函数去调用close。

在析构函数中面对异常时,请记住:
(1)假如析构函数中抛出了异常,那么你的系统将变得非常危险,也许很长时间什么错误也不会发生;但也许你的系统有时就会莫名奇妙地崩溃而退出了,而且什么迹象也没有,不利于系统的错误排查;
(2)析构函数禁止抛出异常。如果析构函数发生异常,不要让异常逃离析构函数,析构函数应该捕捉任何异常,不传播或结束程序;
(3)如果客户需要对某个操作函数运行期间抛出的异常作出反应,那么class应该提供一个普通函数(而非在析构函数中)执行该操作。


参考文献

[1] Effective c++学习笔记——条款08:别让异常逃离析构函数
[2] 构造函数、析构函数抛出异常的问题

C++ 析构函数不要抛出异常相关推荐

  1. C++中构造函数和析构函数可以抛出异常吗?

    C++中构造函数和析构函数可以抛出异常吗? 一.  析构函数 参照<Effective C++>中条款08:别让异常逃离析构函数.  总结如下: 1. 不要在析构函数中抛出异常!虽然C++ ...

  2. 构造函数和析构函数中抛出异常

    文章目录 1 构造函数中抛出异常 2 析构函数中的异常 1 构造函数中抛出异常 如果构造函数中抛出异常会发生什么情况? 构造函数中抛出异常: 构造过程立即停止. 当前对象无法生成. 析构函数不会被调用 ...

  3. C++ 异常 与 ”为什么析构函数不能抛出异常“ 问题

    C++ 用异常使得可以将正常执行代码和出错处理区别开来. 比如一个栈,其为空时,调用其一个pop 函数,接下来怎么办? 栈本身并不知道该如何处理,需要通知给其调用者(caller),因为只有调用者清楚 ...

  4. 构造函数 和 析构函数 能否抛出异常

    构造函数和析构函数分别管理对象的建立和释放,负责对象的诞生和死亡的过程.当一个对象诞生时,构造函数负责创建并初始化对象的内部环境,包括分配内存.创建内部对象和打开相关的外部资源,等等.而当对象死亡时, ...

  5. Item 8:析构函数不要抛出异常 Effective C++笔记

    Item 8: Prevent exceptions from leaving destructors. 析构函数不要抛出异常 因为析构函数经常被自己主动调用,在析构函数中抛出的异常往往会难以捕获,引 ...

  6. 27.能否在构造函数中抛出异常?析构函数呢?

    首先,我们要明确一点!一个函数执行的过程中,如果抛出异常,会导致函数提前终止! 在C++构造函数中,既需要分配内存,又需要抛出异常时要特别注意防止内存泄露的情况发生.因为在构造函数中抛出异常,在概念上 ...

  7. NO.7:别让异常逃离析构函数

    1.析构函数绝对不要吐出异常,如果一个析构函数可能抛出异常,析构函数应该捕获任何异常,然后要么吞下它们或者退出程序 2.如果用户需要对析构内的可能抛出异常的操作做出反应,则应该将操作放入除析构函数外的 ...

  8. C++析构函数不能失败的4个理由

    析构函数非常特殊,编译器将在不同的上下文中自动调用.由于其特殊性,C++标准有如下建议:在栈展开过程中,如果析构函数抛异常,terminate函数将被调用.因此,析构函数应该总是能够捕获异常,并且不会 ...

  9. C++构造函数与析构函数

    几乎所有的面向对象的编程语言都保护构造函数与析构函数,好学者就要举手了,啥情况啊,Java里面不就没有析构函数嘛.好吧,你问倒我了,不过可以认为Java的垃圾自动回收机制实现了对象析构的功能.管他呢, ...

最新文章

  1. python写一个通讯录step by step V3.0
  2. 没有插件的 Chrome 是没有灵魂的
  3. 过桥问题c语言程序,盏灯过桥游戏
  4. android 自定义图形,Android自定义View之图形图像(模仿360的刷新球自定
  5. freemarker写select组件报错总结(二)
  6. airodump-ng wlan0mon扫描不到网络_MySQL ProxySql 由于漏洞扫描导致的 PROXYSQL CPU 超高...
  7. exe打包工具哪个最好_一键分发工具哪个最好用?这款30万人都在用,很优秀!...
  8. python如何使用本地数据库_使用Python在虚拟机上怎么连接本地数据库
  9. http协议与php关系,HTTP协议的由来
  10. 如何将dwt模板移到php,ECSHOP模板文件(dwt)在线编辑器 | Jacklee的博客
  11. 作为IT男必须会Linux服务器被攻击后如何处理!网友:这个要会!
  12. ssh安装与配置(详解版)
  13. qt信号发送间隔短而槽耗时多_Qt 信号和槽机制详解
  14. 四元数与欧拉角之间的换算关系
  15. C# Gridview 固定表头及表尾
  16. 【安信可IDE 1.5模板专题2】安信可windows一体化环境IDE V1.5 ESP8266 SDK二次开发直连阿里云飞燕平台,天猫精灵语音控制;
  17. 访问限制:由于对必需的库E:\j2sdk\jre\lib\rt.jar具有一定限制,因此无法访问类型JFrame
  18. python制作简易动态二维码
  19. word选择性粘贴没有HTML选项,Word选择性粘贴如何使用?Word选择性粘贴的快捷键是什么?...
  20. 初谈“信息安全审计”

热门文章

  1. 简单的移动端打开pdf文件。
  2. C#DataTable2Json(附时间格式化)
  3. 数据流图技术相关基础知识
  4. 【Nodejs开发】第2章 网站首页的布局
  5. Cisco ASA ‘LU allocate xlate failed’排错一例
  6. 影响局域网速度的因素
  7. 基于Token的WEB后台登录认证机制(并讲解其他认证机制以及cookie和session机制)
  8. Linux中shell编程的for循环用法
  9. 多线程之 interrupt,interrupted,isInterrupted 方法区别
  10. [Python] L1-053 电子汪-PAT团体程序设计天梯赛GPLT