这章非常容易理解:因为C++并不禁止析构函数吐出异常,只是不鼓励这样做而已。

一、原因

假设我们有10个装着鸡蛋的容器,而且现在我们还想着把它在析构函数打烂。

class Egg {
public :
...
~Egg() {
// 这里可能出错,导致蛋打不烂
}
}; void foo() {
vector<Egg> v // 假设v中间有10个Egg
....
} // v在这里被自动销毁

如果我们在销毁10个鸡蛋的过程中,在析构第一个鸡蛋的时候,有个异常被抛出,按照销毁机制,后续的9个鸡蛋还是需要被销毁的(否则鸡蛋保存的任何资源都会发生泄漏)。

但是如果后面的鸡蛋仍然抛出异常,在两个异常同时存在的情况下,C++程序会结束执行或者出现不明确的行为。

就算是使用STL的其他容器,还是会发生同样的问题。

为什么呢?因为C++不鼓励析构函数吐出异常。

二、详解

为了方便上面的原因理解,我们可以来尝试一下的例子:

class DB {
public :
....
static DB create() ;//函数返回DB对象
void close(); //关闭数据库的联机,失败则抛出异常
}

如果为了方便其他人员使用DB类,防止在调用DB的时候忘记关闭连接,那么我们可以贴心一下:

class DBC {
public :
....
~DBC() { // 确保每次调析构的时候都会关闭连接
db.close();
}
private:
DB db;
}

其他人直接使用DBC的类就好了,但是如果这样写,就会出现章一种的问题了,如果析构中抛出了异常怎么办?

我们可以用两种方法来解决:

方法1:

DBC::~DBC() {
try {(db.close();) } //检查异常
catch (...) {
std::abort(); //如果catch到了异常,那么直接强迫结束程序
}
}

方法2:

DBC::~DBC() {
try {(db.close();) } //检查异常
catch (...) {
... //如果catch到了异常,记录对close调用失败
}
}

上面两种方法似乎都会异常进行了"提示",但是都无法针对“导致异常”的情况作出处理。

因此,我们可以考虑重新设计DBC类:

class DBC {
public :
....
void close() {
db.close();
closed = true;
}
~DBC() { // 确保每次调析构的时候都会关闭连接
if (!closed) {
try {db.close();}
catch(...) {
...// 记录异常
}
}
}
private:
DB db;
bool closed;
}

■总结:

1.析构函数绝对不要吐出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该要能捕捉任何异常,然后“吞下异常”或者终止程序。

2.如果需要对某个操作函数运行期间抛出的异常作出反应,那么class应该提供一个普通函数(而不是在析构函数中)执行该操作。

[Effective C++ --008]别让异常逃离析构函数的更多相关文章

  1. Effective C++ .08 别让异常逃离析构函数

    异常不怎么用,C++能自己控制析构过程,也就有这个要求了.容器不能完全析构其中的元素真是太危险了

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

    1.别让异常逃离析构函数的原因 <Effective C++>第三版中条款08建议不要在析构函数中抛出异常,原因是C++异常机制不能同时处理两个或两个以上的异常.多个异常同时存在的情况下, ...

  3. c++之别让异常逃离析构函数

    关于 本文代码演示环境: VS2017. 代码写的够不规范,目的是为了缩短文章篇幅. 本文主要是为了加深印象,写了好多次的代码,还是忘记了这茬.... 之前上传到github的代码会慢慢改过来. 本文 ...

  4. EC读书笔记系列之4:条款8 别让异常逃离析构函数

    条款8 别让异常逃离析构函数 记住: ★析构函数绝对不要吐出异常.若一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们(不传播)或结束程序. ★若客户需对某个操作函数运行期间 ...

  5. Effective C++_笔记_条款08_别让异常逃离析构函数

    (整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) C++并不禁止析构函数吐出异常,但它不鼓励你这样做.考虑如下代码 ...

  6. 《Effective C++》——条款08:别让异常逃离析构函数

    考虑如下代码: class Widget{ public: ... ~Widget(){...}//假设这个可能吐出一个异常 }; void doSomething() { std::vector&l ...

  7. Effective C++ 条款八 别让异常逃离析构函数

    class DBConn //这个class用来管理DBConnction对象 { public:   //自己设计一个新的DBConn接口 方法3 void close() { db.close() ...

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

    析构函数绝对不要吐出异常.如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们(不传播)或结束程序. 如果客户需要对某个操作函数运行期间抛出的异常作出反应,那么class应 ...

  9. EC笔记,第二部分:8.别让异常逃离析构函数

    1.为何析构函数不应该抛出异常?    有两种情况:    1).假设析构函数中有众多语句,而第一条语句抛出异常(或者其他语句),那么抛出异常以后的语句就得不到执行.而通常我们在析构函数中写的是清理资 ...

随机推荐

  1. Ruby基础类型,动态特性,代码块

    #Ruby内置基础数据类型 NilClass,TureClass,FalseClass,Time,Date,String,Range,Struct,Array,Hash #Numerice 1.分为I ...

  2. [面试题] for() while() 条件判断 赋值问题

    http://group.jobbole.com/7963/#comm-11311 [题目]:下列for循环的循环体执行次数为 for(int i=10, j=1; i=j=0; i++, j--)( ...

  3. POJ 1947-Rebuilding Roads(树形背包)

    题意: 一个树求得到一个节点数为p的子树,最小需要删除的边数. 分析:父节点到儿子这条边,删或不删,背包问题. #include <map> #include <set> #i ...

  4. [OFBiz]简介 二

    1. 执行ant run-install后,生成了55个ofbiz的jar.加上最初的E:\apache-ofbiz-10.04\framework\entity\lib\ofbiz-minerva. ...

  5. WCF SOA --- AJAX 跨域请求处理 CORS for WCF

    一.问题        跨域请求无法处理的问题,由于为了阻止恶意的网站通过JS脚本来窃取正常网站受保护的资源.所由所有的浏览器的默认策略是阻止XmlHttpRequest的跨域的异步请求. 但是对于一 ...

  6. 用通俗的例子解释OAuth和OpenID的区别【原】

    什么是OAuth(Wiki) 什么是OpenID(Wiki) 详细的定义可以看wiki,下面举个例子说说我的理解 现在很多网站都可以用第三方的账号登陆,比如,现在我要登录淘宝买东西,而如果我没有淘宝的 ...

  7. Esper系列(十三)Splitting and Duplicating Streams

    功能:将同一个事件流分发到其它的事件流中. 格式: 1  ; i < events.length; i++) {   78              EventBean event = even ...

  8. [转]带花树,Edmonds's matching algorithm,一般图最大匹配

    看了两篇博客,觉得写得不错,便收藏之.. 首先是第一篇,转自某Final牛 带花树……其实这个算法很容易理解,但是实现起来非常奇葩(至少对我而言). 除了wiki和amber的程序我找到的资料看着都不 ...

  9. Oracle-查看oracle是否有表被锁

    问题现象: 查看oracle是否有表被锁 解决方法: select sid,serial#,program,terminal,username,b.object_id,c.object_name  f ...

  10. [css]display: table-cell,用div做分列布局

    table-cell我们却能用得到,而且是用它来干一件很重要的事情——多列布局. 多列布局在css中有多重要就不用我说了吧,传统模式下大家都使用float来解决这一问题,但是float写出来的东西代码 ...