***************************************转载请注明出处:http://blog.csdn.net/lttree********************************************
二、Constructors,Destructors and Assignment Operators
Rule 08:Prevent exceptions from leaving destructors
规则08:别让异常逃离析构函数
C++并不禁止析构函数吐出异常,但它不鼓励你这样做。
首先,看下面这个例子:
class Widget { public: ... ~Widget( ) { ... } // 假设这里可能吐出异常 }; void doSomething() { std::vector<Widget> v; ... } // v在这里被销毁
2.即使知道C++讨厌,但我们无法避免要面对它
如果析构函数必须执行某个动作,但这个动作可能导致异常,怎么办呢?
依旧用书上的例子,用一个类来负责数据库连接:
class DBConnection { public: ... static DBConnection create(); void close(); };
但是为了确保客户不忘记在 DBConnection 对象上调用close(),一个合理的想法是创建一个用来管理DBConnection资源的class并在其析构函数中调用close,如同下面这种:
class DBConn { public: ... ~DBConn() { db.close(); } private: DBConnection db; };
{ DBConn dbc( DBConnection::create() ); ... }<span style="white-space:pre"> </span>// 在块结束时DBConn对象被销毁,因为自动为DBConnection对象调用close
对于这个情况有两个方案来搞定:
<1>plan01:
如果 close 抛出异常就结束程序。通常就通过调用abort完成:
DBConn::~DBConn( ) { try{ db.close(); } catch( ... ) {<span style="font-family: Arial, Helvetica, sans-serif;"> </span>
<pre name="code" class="cpp"> 制作运转记录,记下对close的调用失败;std::abort(); }}
<2>plan02:
吞下因调用close而发生的异常
DBConn::~DBConn( ) { try { db.close(); } catch ( ... ) { 制作运转记录,记下对close的调用失败; } }
3.更好的方法?
上面两种方法都是因为 无法对 导致close抛出异常 的情况作出反应。
所以,更好的办法,就是 重新设计 DBConn 的接口,比如:
class DBConn { public: ... void close()<span style="white-space:pre"> </span>// 供用户使用的新函数 { db.close(); closed = true; } ~DBConn() { if( !closed ) { try {<span style="white-space:pre"> </span>// 如果用户没有关闭连接,则执行关闭连接 db.close(); } catch ( ... ) {<span style="white-space:pre"> </span>// 如果关闭动作失败 制作运转记录,记下对close的调用失败; .... } } } private: DBConnection db; bool closed; };
这个方案的优点,把调用close责任给到用户手上,但是还设置了一个双保险,让程序更加安全。
4.请记住:
★ 析构函数绝对不要吐出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们(让它们不能传播出去)或者 结束程序。
★ 如果客户需要对某个操作函数运行期间抛出异常做出反应,那么class应该提供一个普通函数(而非在析构函数中)执行该操作。
***************************************转载请注明出处:http://blog.csdn.net/lttree********************************************
原文地址:http://blog.csdn.net/lttree/article/details/41007475