考虑如下代码:

class Widget{
public:
...
~Widget(){...}//假设这个可能吐出一个异常
};
void doSomething()
{
std::vector<Widget>v;
}//v在这里被销毁

当vector v被销毁,它有责任销毁其内含的所有Widgets。假设v内含十个Widgets,而在析构第一个元素期间,有个异常被抛出。其他九个Widgets还是应该被销毁,因此v应该调用它们各个析构函数。假设在调用期间,第二个Widget析构函数又抛出异常。现在有两个同时作用的异常,这对C++而言太多了。在这两个异常同时存在的情况下,程序若不是结束执行就是导致不明确行为。本例中它会导致不明确的行为。容器或array并非遇上麻烦的必要条件,只要析构函数突出异常,即使并非使用容器或arrays,程序也可能过早结束或出现不明确行为。

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

class DBConnection
{
public:
...
static DBConnection create(); //返回DBConnection对象
void close(); //关闭联机;失败则抛出异常
}

为确保不忘记在DBConnection对象上调用close(),可以创建一个用来管理DBConnection资源的class,并在其析构函数中调用close。:

class DBConn
{
public:
...
~DBConn()
{
db.close();
}
private:
DBConnection db;
};

客户可以写这样的代码:

{
DBConn dbc(DBConnection::create());
...
}//DBConn对象被销毁,自动为DBConnection对象调用close

如果该调用失败,DBConn析构函数会传播该异常,也就是允许它离开这个析构函数。那会造成难以驾驭的麻烦。

两个办法可以避免这一问题。DBConn的析构函数可以:

1. 如果close抛出异常就结束程序。通常通过调用abort完成:

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

“强迫结束程序”可以组织异常从析构函数传播出去(那会导致不明确行为)。

2. 吞下因调用close而发生的异常:

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

一般而言,将异常吞掉是个坏主意,因为它压制了“某些动作失败”的重要信息!但是有时候吞下异常也比“草率结束程序”或“不明确行为带来的风险”好。为了让这称为一个可行方案,程序必须能够继续可靠地执行,及时在遭遇并忽略一个错误之后。

然而,这两个办法都无法对“导致抛出异常”的情况作出反应。

一个较佳策略是重新设计DBConn接口,使其客户有机会对可能出现的问题作出反应。例如DBConn自己可以提供一个close函数,因而赋予客户一个机会得以处理“因该操作而发生的异常”。

DBConn也可以追踪其所管理之DBConnection是否已被关闭,并在答案为否的情况下由其析构函数关闭。然而如果DBConnection析构函数调用close失败,我们又将退回“强迫结束程序”或“吞下异常”的老路:

class DBConn{
public:
...
void close()
{
db.close();
closed = true;
}
~DBConn()
{
if (!closed)
{
try{
db.close();
}
catch(...)
{
}
}
}
private:
DBConnection db;
bool closed;
};

如果某个操作可能在失败时抛出异常,而又存在某种需要处理该异常,那么这个异常必须来自析构函数以外的某个函数。因为析构函数吐出异常总会带来“过早结束程序”或“发生不明确行为的风险”。

《Effective C++》——条款08:别让异常逃离析构函数的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  8. Effective C++ -----条款29:为“异常安全”而努力是值得的

    异常安全函数(Exception-safe functions)即使发生异常也不会泄露资源或允许任何数据结构败坏.这样的函数区分为三种可能的保证:基本型.强烈型.不抛异常型. “强烈保证”往往能够以c ...

  9. [Effective C++ --008]别让异常逃离析构函数

    这章非常容易理解:因为C++并不禁止析构函数吐出异常,只是不鼓励这样做而已. 一.原因 假设我们有10个装着鸡蛋的容器,而且现在我们还想着把它在析构函数打烂. class Egg { public : ...

随机推荐

  1. JavaScript实现自适应窗口大小的网页

    <!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content ...

  2. c# 执行批处理文件

    ProcessStartInfo proc = new ProcessStartInfo(); proc.UseShellExecute = false; proc.CreateNoWindow = ...

  3. JavaScript:学习笔记(3)——正则表达式的应用

    JavaScript:正则表达式的应用 应用正则表达式对象RegExp 创建正则表达式 JavaScript中使用RegExp对象来表述一个正则表达式.使用正则表达式之前首先要创建一个RegExp对象 ...

  4. 每天一个Linux命令(38)top命令

     top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器.       (1)用法:       用法:  top  [参数] top是 ...

  5. JAVA 文件转字节数组转字符串

    public static void main(String[] args) throws IOException { byte[] bytes = FileUtils.readFileToByteA ...

  6. 【鸟哥的Linux私房菜】笔记

    操作系统核心的功能! 驱动程序与操作系统的关系 2. [计算机组成之组件] 3.CPU实际要处理的数据完全来自于主存储器,这是一个很重要的概念! 4.CPU是整个计算机系统最重要的部分,那么目前世界上 ...

  7. Oracle创建新用户并将某一用户的表导进来

    一.建立测试账号 进入sqlplus(以管理员身份) sqlplus / as sysdba; create user user1 identified by user1;  //创建用户名并指定密码 ...

  8. iOS_UIAlertController

    ViewController.m文件 #import "ViewController.h" @interface ViewController () // @property (n ...

  9. unity json解析IPA后续

    以前说到的,有很大的限制,只能解析简单的类,如果复杂的就会有问题,从老外哪里看到一片博客,是将类中的list   等复杂对象序列化, using UnityEngine; using System.C ...

  10. Kubernetes Rook

    Rook Rook 是一个开源的cloud-native storage编排, 提供平台和框架:为各种存储解决方案提供平台.框架和支持,以便与云原生环境本地集成. Rook 将存储软件转变为自我管理. ...