《Effective C++》——条款08:别让异常逃离析构函数
考虑如下代码:
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:别让异常逃离析构函数的更多相关文章
- Effective C++ -----条款08: 别让异常逃离析构函数
析构函数绝对不要吐出异常.如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们(不传播)或结束程序. 如果客户需要对某个操作函数运行期间抛出的异常作出反应,那么class应 ...
- Effective C++ 条款八 别让异常逃离析构函数
class DBConn //这个class用来管理DBConnction对象 { public: //自己设计一个新的DBConn接口 方法3 void close() { db.close() ...
- EC读书笔记系列之4:条款8 别让异常逃离析构函数
条款8 别让异常逃离析构函数 记住: ★析构函数绝对不要吐出异常.若一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们(不传播)或结束程序. ★若客户需对某个操作函数运行期间 ...
- Effective C++_笔记_条款08_别让异常逃离析构函数
(整理自Effctive C++,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) C++并不禁止析构函数吐出异常,但它不鼓励你这样做.考虑如下代码 ...
- Effective C++ .08 别让异常逃离析构函数
异常不怎么用,C++能自己控制析构过程,也就有这个要求了.容器不能完全析构其中的元素真是太危险了
- Effective C++ 条款08:别让异常逃离析构函数
1.别让异常逃离析构函数的原因 <Effective C++>第三版中条款08建议不要在析构函数中抛出异常,原因是C++异常机制不能同时处理两个或两个以上的异常.多个异常同时存在的情况下, ...
- c++之别让异常逃离析构函数
关于 本文代码演示环境: VS2017. 代码写的够不规范,目的是为了缩短文章篇幅. 本文主要是为了加深印象,写了好多次的代码,还是忘记了这茬.... 之前上传到github的代码会慢慢改过来. 本文 ...
- Effective C++ -----条款29:为“异常安全”而努力是值得的
异常安全函数(Exception-safe functions)即使发生异常也不会泄露资源或允许任何数据结构败坏.这样的函数区分为三种可能的保证:基本型.强烈型.不抛异常型. “强烈保证”往往能够以c ...
- [Effective C++ --008]别让异常逃离析构函数
这章非常容易理解:因为C++并不禁止析构函数吐出异常,只是不鼓励这样做而已. 一.原因 假设我们有10个装着鸡蛋的容器,而且现在我们还想着把它在析构函数打烂. class Egg { public : ...
随机推荐
- Android开发视频教程
http://study.163.com/course/courseMain.htm?courseId=207001 目录 章节1第一季 课时1课程介绍15:17 课时2Android历史15 ...
- BAPI: TRANSACTION_BEGIN的作用
大概知道是启动一个新会话, CALL FUNCTION 'TRANSACTION_BEGIN' 业务数据处理, CALL FUNCTION 'TRANSACTION_END' 详细功能不清楚. CLE ...
- ACM解题之(ZOJ 2212) Argus
题目来源: 点击打开题目 题目翻译: 数据流是实时的,连续的,有序的项目序列.一些例子包括传感器数据,互联网流量,金融代码,在线拍卖以及诸如网络使用日志和电话记录之类的交易日志.同样,对流进行的查询在 ...
- python之数据的序列化
参考博客:http://www.cnblogs.com/yyds/p/6563608.html 数据的序列化功能表 json.dumps() 将python数据类型转换为(json)字符串 json. ...
- 曾经跳过的坑----jQuery mouseover与mouseenter,mouseout与mouseleave的区别
mouseover与mouseenter 不论鼠标指针穿过被选元素或其子元素,都会触发 mouseover 事件. 只有在鼠标指针穿过被选元素时,才会触发 mouseenter 事件. mouseou ...
- PL/SQL连接ORACLE失败,ORA-12154: TNS: could not resolve the connect identifier specified
项目需要使用ORACLE,安装了oracle之后,使用PL/SQL连接,先是提示NOT logger ,后续不知道改了什么提示解析服务器id失败,重新装了之后更狠的直接来了个空白提示 一.安装PLS ...
- Yii2 使用 Beanstalk 队列系统
参考网址: Beanstalk:https://github.com/kr/beanstalkd Beanstalk console:https://github.com/ptrofimov/bean ...
- Raspberry 2B && Ubuntu mate 16.04 && *** 完美透明代理
Raspberry 2B && Ubuntu mate 16.04 && *** 完美透明代理 关键词:Raspberry 2B, Ubuntu mate 16.04 ...
- INSPIRED启示录 读书笔记 - 第6章 招聘产品经理
产品经理应有的特质 个人素质和态度:技术可以学习,素质却难以培养,有些素质是成功的产品经理必不可少的 对产品的热情:对产品有一种本能的热爱,是夜以继日克服困难.完善产品的动力 用户立场:能换位思考,能 ...
- IEnumerable的一些基本方法 补充
接上一篇,我们发现两表连接方式默认为内连接,而我们在SQL中常用到的左连接没有封装方法.换句话说,微软放弃两表左连或右连的这种做法(只有在2个表都存在值时,这样的连接才有意义). 如果要实现表的左连接 ...