读书笔记_Effective_C++_条款二十九:为“异常安全”而努力是值得的
还是举书上的例子:
void PrettyMenu::changeBackground(std::istream& imgSrc)
{
lock(&mutex);
delete bgImage;
++ imageChanges;
bgImage = new Image(imgSrc);
unlock(&mutex);
}
这段代码大致的意思就是改变背景图片,删掉旧的背景图片,记录修改次数,然后创建新的背景图片。考虑到多线程操作,所以这里用了lock和unlock。
但这里会出现问题,因为并不是每次new都会成功的,有可能抛出异常,一旦抛出异常,unlock就没有执行了,这样资源就会一直处于lock状态,而无法继续操作了。另一方面,虽然本次改变背景的操作的失败了,但imageChanges仍然自增了一次,这就不符合程序员设计的初衷了。
有读者就会想,那还不简单,加上try…catch块就行了,像这样:
void PrettyMenu::changeBackground(std::istream& imgSrc)
{
try
{
lock(&mutex);
delete bgImage;
bgImage = new Image(imgSrc);
++ imageChanges;
unlock(&mutex);
}
catch (Exception* e)
{
unlock(&mutex);
}
}
在catch里面写上unlock函数,另外,调换imageChanges的位置,在new之后再对齐自增。这样做固然可以,但回想一下,我们在条款十三和条款十四做了资源管理类,让类的析构函数自动替我们完成这个操作,不是会更好吗?像这样:
class PrettyMenu
{
…
shared_ptr<Image> bgImage;
…
}
void PrettyMenu::changeBackground(std::istream& imgSrc)
{
Lock m1(&mutex);
bgImage.reset(new Image(imgSrc));
++ imageChanges;
}
这样,即使抛出了异常,锁资源还有imageChanges都保证是异常发生之前的状态。
现在上升到理论的高度,异常安全性要做到:
1. 不泄漏任何资源
2. 不允许数据败坏
带异常安全性的函数会提供三个保证之一:
1. 基本承诺:如果异常被抛出,程序内的任何事物仍然保持在有效状态下。没有任何对象或者数据结构会因此被破坏。比如上例中本次更换背景图失败,不会导致相关的数据发生破坏。
2. 强烈保证:在基本承诺的基础上,保证成功就是完全成功,失败也能回到之前的状态,不存在介于成功或失败之间的状态。
3. 不抛出异常:承诺这个代码在任何情况下都不会抛出异常,但这只适用于简单的语句。
强烈保证有一种实现方法,那就是copy and swap。原则就是:在修改这个对象之前,先创建它的一个副本,然后对这个副本进行操作,如果操作发生异常,那么异常只发生在这个副本之上,并不会影响对象本身,如果操作没有发生异常,再在最后进行一次swap。
void PrettyMenu::changeBackground(std::istream& imgSrc)
{
Lock m1(&mutex);
弄一个临时的tempBgImage
对tempBgImage进行操作
swap(tempBgImage, bgImage);
++ imageChanges;
}
copy-and-swap策略关键在于“修改对象数据的副本,然后在一个不抛异常的函数中将修改后的数据和原件置换”。它确实提供了强异常安全保障,但代价是时间和空间,因为必须为每一个即将被改动的对象造出副本。另外,这种强异常安全保障,也会在下面的情况下遇到麻烦:
void someFunc()
{
f1();
f2();
}
f1()和f2()都是强异常安全的,但万一f1()没有抛异常,但f2()抛了异常呢?是的,数据会回到f2()执行之前的状态,但程序员可能想要的是数据回复到f1()执行之前。要解决这个问题就需要将f1与f2内容进行融合,确定都没有问题了,才进行一次大的swap,这样的代价都是需要改变函数的结构,破坏了函数的模块性。如果不想这么做,只能放弃这个copy-and-swap方法,将强异常安全保障回退成基本保障。
类似于木桶效应,代码是强异常安全的,还是基本异常安全的,还是没有异常安全,取决于最低层次的那个模块。换言之,哪怕只有一个地方没有考虑到异常安全,整个代码都不是异常安全的。
最后总结一下:
1. 异常安全函数是指即使发生异常也不会泄漏资源或者允许任何数据结构败坏。这样的函数区分为三种可能的保证:基本型、强烈型、不抛异常型。
2. 强烈保证往往可以通过copy-and-swap实现出来,但“强烈保证”并非对所有函数都可以实现或具备现实意义。
3. 异常安全保证通常最高只等于其所调用之各个函数的异常安全保证中的最弱者。
读书笔记_Effective_C++_条款二十九:为“异常安全”而努力是值得的的更多相关文章
- 读书笔记_Effective_C++_条款三十九:明智而审慎地使用private继承
private继承的意义在于“be implemented in turns of”,这个与上一条款中说的复合模型的第二层含义是相同的,这也意味着通常我们可以在这两种设计方法之间转换,但书上还是更提倡 ...
- 读书笔记_Effective_C++_条款四十九:了解new_handler的行为
本章开始讨论内存分配的一些用法,C/C++内存分配采用new和delete.在new申请内存时,可能会遇到的一种情况就是,内存不够了,这时候会抛出out of memory的异常.有的时候,我们希望能 ...
- 读书笔记_Effective_C++_条款二十八:避免返回handlers指向对象内部成分
举个例子: class Student { private: int ID; string name; public: string& GetName() { return name; } } ...
- 读书笔记_Effective_C++_条款二十六:尽可能延后变量定义式的出现时间
这个条款从字面意思还是很好理解的,就是在使用这个变量前才去定义,而不是很早就定义了它,而在很后面的时候才去使用.这个条款只适用于对变量声明位置没有要求的语言,比如C++.对于像C或者一些脚本语言,语法 ...
- 读书笔记_Effective_C++_条款二十五: 考虑写出一个不抛出异常的swap函数
在之前的理论上调用对象的operator=是这样做的 void swap(A& x) { std::swap(a, x.a); } A& operator=(const A& ...
- 读书笔记_Effective_C++_条款二十四: 若所有参数皆需类型转换,请为此采用non-member函数
class A { private: int a; public: A(int x) :a(x){} A operator*(const A& x) { return A(a*x.a); } ...
- 读书笔记_Effective_C++_条款二十二:将成员变量声明为private
1.格式统一 在调用的时候,不会去想有没有(),一律是有get(),或者set()之类的. 2.封装 能直接访问得越少,表明封装性越高, 封装性越高,我们的顾虑就少了, 例如:我们a.data*0.9 ...
- 读书笔记_Effective_C++_条款三十:了解inline的里里外外
学过基本程序课的同学都知道,inline是内联的关键字,它可以建议编译器将函数的每一个调用都用函数本体替换.这是一种以空间换时间的做法.把每一次调用都用本体替换,无疑会使代码膨胀,但可以节省函数调用的 ...
- 读书笔记_Effective_C++_条款三十二:确定你的public继承继承塑模出is-a关系
这一条款是说的是公有继承的逻辑,如果使用继承,而且继承是公有继承的话,一定要确保子类是一种父类(is-a关系).这种逻辑可能与生活中的常理不相符,比如企鹅是生蛋的,所有企鹅是鸟类的一种,直观来看,我们 ...
随机推荐
- linux设置时区同步时间
linux设置时区同步时间 一.运行tzselect sudo tzselect 在这里我们选择亚洲 Asia,确认之后选择中国(China),最后选择北京(Beijing) 如图: 二.复制文件 ...
- C# 开发(创蓝253)手机短信验证码接口
创蓝253: https://www.253.com/ #region 获取手机验证码(创蓝253) /// <summary> /// 获取手机验证码(创蓝253) /// </s ...
- 禁用quartz自动检查更新
禁用quartz自动检查更新的3种方法 1, <bean id="startQuertz" lazy-init="false" autowire=&quo ...
- Java集合里的一些“坑”
这里主要谈下Java集合在使用中容易被忽略.又容易出现的两个“坑”,一个是集合与数组互相转换,另一个是集合遍历删除.主要通过代码演示. 一.集合与数组互相转换中的“坑” //Test1.java pa ...
- win10产品密钥 win10专业版激活码key
转载地址:http://www.xitongcheng.com/jiaocheng/xtazjc_article_35407.html https://blog.csdn.net/WangJianku ...
- [ python ] 变量及基础的数据类型
python2 和 python3 不同的编码方式 python2 默认编码方式是 ascii码 python3 默认编码方式是 utf-8 具体表现为:当 python3 和 python2 在打印 ...
- PC端网站跳转手机端网站
<SCRIPT LANGUAGE="JavaScript"> function mobile_device_detect(url) { var thisOS=navig ...
- 洛谷P1615 西游记公司 题解
题目传送门 这道题题面写得非常好. 但好像并没有什么ruan用. 这道题貌似就是把时间差求出来,乘上猪八戒能偷的电脑数就好了.(注意long long) #include<bits/stdc++ ...
- Centos bash: make: command not found
一般出现这个bash: make: command not found提示,是因为安装系统的时候使用的是最小化mini安装,系统没有安装make.vim等常用命令,直接yum安装下即可: yum -y ...
- PHP原理之变量
作者: Laruence( ) 本文地址: http://www.laruence.com/2008/08/22/412.html 转载请注明出处 或许你知道,或许你不知道,PHP是一个弱类型,动 ...