上一篇:C++箴言:多态基类中将析构函数声明为虚拟 >>
C++箴言:防止因异常而离开析构函数
| class Widget { public: ... ~Widget() { ... } // assume this might emit an exception }; void doSomething() { std::vector<Widget> v; ... } // v is automatically destroyed here |
当 vector v 被析构时,它有责任销毁它包含的所有 Widgets。假设 v 中有十个 Widgets,在销毁第一个的时候,抛出一个异常。其他 9个 Widgets 仍然必须被销毁(否则他们持有的任何资源将被泄漏),所以 v 应该调用它们的析构函数。但是假设在这个调用期间,第二个 Widgets 的析构函数又抛出一个异常。现在有两个异常同时在活动中,对于 C++ 来说这太多了。在非常巧合的条件下发生这样两个同时活动的异常,程序的执行会终止或者引发未定义行为。在本例中,将引发未定义行为。与此相同,使用任何标准库容器(比如,list,set),任何 TR1中的容器,甚至是一个数组,都可能会引发未定义问题。并非必须是容器或数组才会陷入麻烦。程序夭折或未定义行为是析构函数引发异常的结果,即使没有使用容器或数组也会如此。C++ 不喜欢引发异常的析构函数。 这比较容易理解,但是如果你的析构函数需要执行一个可能失败而抛出异常的操作,该怎么办呢?例如,假设你与一个数据库连接类一起工作:
| class DBConnection { public: ... static DBConnection create(); // function to return // DBConnection objects; params // omitted for simplicity void close(); // close connection; throw an }; // exception if closing fails |
为了确保客户不会忘记调用 DBconnection 对象的 close,一个合理的主意是为 DBConnection 建立一个资源管理类,在它的析构函数中调用 close。这样的资源管理类将在以后的文章中探讨,但在这里,只要认为这样一个类的析构函数看起来像这样就足够了:
| class DBConn { // class to manage DBConnection public: // objects ... ~DBConn() // make sure database connections { // are always closed db.close(); } private: DBConnection db; }; |
它允许客户像这样编程:
| { // open a block DBConn dbc(DBConnection::create()); // create DBConnection object // and turn it over to a DBConn // object to manage ... // use the DBConnection object // via the DBConn interface } // at end of block, the DBConn // object is destroyed, thus // automatically calling close on // the DBConnection object |
既然能成功地调用 close 那就好了,但是如果这个调用导致了异常,DBConn 的析构函数将散播那个异常,也就是说,它将离开析构函数。这就产生了问题,因为析构函数抛出了一个烫手的山芋。
有两个主要的方法避免这个麻烦。DBConn 的析构函数能:
终止程序 如果 close 抛出异常,调用 abort。
| DBConn::~DBConn() { try { db.close(); } catch (...) { make log entry that the call to close failed; std::abort(); } } |
如果程序在析构过程遭遇到错误后不能继续运行,这就是一个合理的选择。它有一个好处是:如果允许从析构函数散播异常可能会引起未定义行为,这样就能防止它发生。也就是说,调用 abort 就预先防止了未定义行为。
抑制这个异常 起因于调用 close:
| DBConn::~DBConn() { try { db.close(); } catch (...) { make log entry that the call to close failed; } } |
通常,抑制异常是一个不好的主意,因为它会隐瞒重要的信息——某些事情失败了!可是,有些时候,抑制异常比冒程序夭折或未定义行为的风险更可取。程序必须能够在遭遇到错误并忽略之后还能继续可靠地执行,这才能成为一个可行的选择。
这些方法都不太吸引人。它们的问题在于程序无法在第一现场对引起 close 抛出异常的条件做出回应。
一个更好的策略是设计 DBConn 的接口,以使它的客户有机会对可能会发生的问题做出回应。例如,DBConn 能够自己提供一个 close 函数,从而给客户一个机会去处理从那个操作中发出的异常。它还能保持对它的 DBConnection 是否已被关闭的跟踪,如果没有关闭就在析构函数中自己关闭它。这样可以防止连接被泄漏。如果在 DBConnection 的析构函数中调用 close 失败,无论如何,我们还可以再返回到终止或者抑制。
| class DBConn { public: ... void close() // new function for { // client use db.close(); closed = true; } ~DBConn() { if (!closed) { try { // close the connection db.close(); // if the client didn’t } catch (...) { // if closing fails, make log entry that call to close failed; // note that and ... // terminate or swallow } } private: DBConnection db; bool closed; }; |
将调用 close 的责任从 DBConn 的析构函数转移到 DBConn 的客户(同时在 DBConn 的析构函数中包含一个“候补”调用)可能会作为一种肆无忌惮地推卸责任的做法而刺激你。你甚至可以把它看作一个忠告(使接口易于正确使用)的违背。实际上,这都不正确。如果一个操作可能失败而抛出一个异常,而且可能是一个需要处理的异常,这个异常就必须来自非析构函数。这是因为析构函数引发异常是危险的,永远都要冒着程序夭折或未定义行为的风险。在此例中,让客户调用 close 并不是强加给他们的负担,而是给他们一个时机去应付错误,否则他们将没有机会做出回应。如果他们找不到可用到机会(或许因为他们相信不会有错误真的发生),他们可能忽略它,依靠 DBConn 的析构函数为他们调用 close。如果一个错误恰恰发生在那时——如果由 close 抛出——如果 DBConn 抑制了那个异常或者终止了程序,他们将无处诉苦。毕竟,他们无处着手处理问题,他们将不再使用它。
Things to Remember
·析构函数应该永不引发异常。如果析构函数调用了可能抛出异常的函数,析构函数应该捕捉任何异常,然后抑制它们或者终止程序。
·如果类客户需要能对一个操作抛出的异常做出回应,则那个类应该提供一个常规的(非析构函数)函数来完成这个操作。
下一篇:C++箴言:绝不在构造或析构期调用虚函数 >>
相关文章:
- · 全球软件产业五方面动向值得关注
- · 25岁华为一员工病故 之前曾连续加班到深夜
- · 中国软件与IT外包产业以印度为镜
- · 行业观察:软件业寻找“微笑曲线”
- · 美超级黑客改邪归正 专业传授反黑客技术
- · 德职业黑客生活揭秘:合法赚钱
- · 做黑客其实很简单!用IE浏览器想黑就黑
- · 黑客入侵飞机订座系统狂买低价机票
- · 窃取个人账户 电脑黑客大发不义之财
- · 微软正版验证面临信任危机 被指侵犯隐私
- · 新TM可用Email登录 意在MiniFox而非MSN
- · 关于QQ会员在好友列表中显示方式的技巧
- · 不用密码也安全!金山授权保护技术指南
- · QQ网络硬盘使用方法介绍
- · 只防病毒不安全 网络安全还要防哪些?
- · 微软推MSN搜索工具 分页浏览植入IE6
- · 网络安全发展历程:从花瓶到不可或缺
- · 比尔盖茨:微软当前最大挑战是网络安全
- · 玩转网络银行 如何做到便捷安全两不误
- · 网银大众版暗藏漏洞 客户独自面对风险
- · 让你一无所有:病毒新变种瞄准网银账号
- · 美国50网银客户信息被盗 账号可能失窃
- · 25家银行与CFCA签约 网银上“安全锁”
- · 反病毒专家质疑网上电子支付金额限定
- · 网上十大经典黑客软件大曝光
- · 请时刻警惕着!2006年网络安全威胁预测
- · 打造防卫系统 构筑2006网络安全堡垒
- · 安全知识:下一代网络(NGN)安全纵横
- · Google新玩具Google Calendar截图欣赏
- · “谷歌”具有中国气息的宣传Flash发布
- · 无线网攻击工具进攻方法及防范技巧
- · 不得不说 无线网络安全六种简单技巧
- · 无线设备驱动漏洞可能导致黑客入侵
- · 运筹帷幄:安全风险管理方法概述
- · 教你用十大策略保证内网计算机安全
- · 中搜划词搜索删除全攻略
- · 微软警告新木马出现:警惕不明.mdb文件
- · 微软发布IE7 RC1版 正式版年底推出
