EffectiveC++学习笔记-条款28|29_allenxguo的博客-程序员秘密

技术标签: C++  c++  异常  effective-c++  

条款28 避免返回handles指向对象内部成分
条款29 为“异常安全”而努力是值得的

避免返回handles指向对象内部成分

  • 避免返回handles(包含references、指针、迭代器)指向对象内部。
    增加封装性、帮助const成员函数的行为像个const。
    例如:operator[]这种就允许访问string、vector个别元素,但是毕竟是个别的例外。

为“异常安全”而努力是值得的

假设有个class用来表现夹带背景图案的GUI菜单,这个class希望用于多线程环境,所以它有个互斥器(mutex)作为并发控制:

class PrettyMenu
{
public:
    //改变背景图像
    void changeBackground(std::istream& imgSrc);
private:
    Mutex mutex; //互斥器
    Image* bgImage; //目前的图像
    int imageChanges; //背景图像被改变的次数
}
//一种实现
void PrettyMenu::changeBackground(std::istream& imgSrc)
{
    lock(&mutex);
    delete bgImage;
    ++ imageChanges;
    bgImage = new Image(imgSrc);
    unlock(&mutex);
}

但这里会出现问题,因为并不是每次new都会成功的,有可能抛出异常,一旦抛出异常,unlock就没有执行了,这样资源就会一直处于lock状态,而无法继续操作了。另一方面,虽然本次改变背景的操作的失败了,但imageChanges仍然自增了一次,这就不对了。

具有异常安全的函数会:

  1. 不泄露任何资源
  2. 不允许数据被破坏

其中保证mutex的方法就是使用,条款14的互斥器

 class PrettyMenu
 {
     …
     shared_ptr<Image> bgImage;
     …
 }
 void PrettyMenu::changeBackground(std::istream& imgSrc)
 {
     Lock m1(&mutex);
     bgImage.reset(new Image(imgSrc));
     ++ imageChanges;
 }

以对象管理资源是常识。即使抛出了异常,锁资源还有imageChanges都保证是异常发生之前的状态。

异常安全性函数的三个保证

  1. 基本承诺:如果异常被抛出,程序内的任何事物仍然保持在有效状态下。没有任何对象或者数据结构会因此被破坏。比如上例中本次更换背景图失败,不会导致相关的数据发生破坏。

  2. 强烈保证:在基本承诺的基础上,保证成功就是完全成功,失败也能回到之前的状态,不存在介于成功或失败之间的状态。

  3. 不抛出异常:承诺这个代码在任何情况下都不会抛出异常,但这只适用于简单的语句。

struct PMImpl
{
    std::tr1::shared_ptr<Image> bgImage;
    int imageChanges;
};

class PrettyMenu
{
...
private:
    Mutex mutex;
    std::tr1::shared_ptr<PMImpl> pImpl;
};

 void PrettyMenu::changeBackground(std::istream& imgSrc)
 {
     using std::swap;
     Lock m1(&mutex);
     //一份副本
     std::tr1::shared_ptr<PMImpl> pNew(new PMImpl(*pImpl));
     //修改副本
     pNew->bgImage.reset(new Image(imgSrc));
     bgImage.reset(new Image(imgSrc));
     ++pNew->imageChanges;

    //将这个修改好的副本  替换成自己的成员
     swap(pImpl, pNew);
 }

opy-and-swap策略关键在于“修改对象数据的副本,然后在一个不抛异常的函数中将修改后的数据和原件置换”。它确实提供了强异常安全保障,但代价是时间和空间,因为必须为每一个即将被改动的对象造出副本。另外,这种强异常安全保障,也会在下面的情况下遇到麻烦:

void someFunc()
{
    f1();
    f2();
}

1()和f2()都是强异常安全的,但万一f1()没有抛异常,但f2()抛了异常呢?是的,数据会回到f2()执行之前的状态,但程序员可能想要的是数据回复到f1()执行之前。要解决这个问题就需要将f1与f2内容进行融合,确定都没有问题了,才进行一次大的swap,这样的代价都是需要改变函数的结构,破坏了函数的模块性。如果不想这么做,只能放弃这个copy-and-swap方法,将强异常安全保障回退成基本保障。

类似于木桶效应,代码是强异常安全的,还是基本异常安全的,还是没有异常安全,取决于最低层次的那个模块。换言之,哪怕只有一个地方没有考虑到异常安全,整个代码都不是异常安全的。

总结

  1. 异常安全函数是指即使发生异常也不会泄漏资源或者允许任何数据结构败坏。这样的函数区分为三种可能的保证:基本型、强烈型、不抛异常型。

  2. 强烈保证往往可以通过copy-and-swap实现出来,但“强烈保证”并非对所有函数都可以实现或具备现实意义。

  3. 异常安全保证通常最高只等于其所调用之各个函数的异常安全保证中的最弱者。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/gx864102252/article/details/74936635

智能推荐

freemarker的classic_compatible_either specify a default value like myoptionalvar!_热心网友徐某某的博客-程序员秘密

最近在整合springboot和freemarker。在这过程中遇到了一个奇怪的问题.我匹配下面这个请求:@RequestMapping(value="/login",method=RequestMethod.GET) public String loginget() { return "pages/pub/login"; }如果访问成功就是访问到了下面这个login

tailwind css_tailwindcss_atwillll的博客-程序员秘密

优势不用命名,不会使css文件变大随意更改与内联样式相比是从预定义的设计系统中选择样式响应式设计可使用hover,focus等hover:鼠标悬停 focus:焦点 active: group-hover:悬停在父元素上时对子元素设置样式。父元素添加group,子元素添加group-hover group-focus:与group-hover一样 focus-within:尽在一个字元素获得焦点时才应用功能类 focus-visible:当一个元素具有焦点且仅在用户使用键盘时才应用功能类 m

linux小白Ubuntu13.04安装完后的一系列活动_DiffenYu的博客-程序员秘密

来兴致了安装了个ubuntu13.04玩一玩linux,遇到了各种菜bi问题。    1.如何调出自带的中文输入法    安装的时候装个bi直接安装了英文版的。进入系统后,发现找不到中文输入法,蛋疼般地只能用拼音谷歌度娘了几下,甚至连我的破手机都拿出来了,只为了能用中文搜索。英文没学好又想装bi的娃伤不起。    不过总算还是找到答案了,在System Setting点击Languag

开发 HTML5 小游戏_weixin_34315189的博客-程序员秘密

Html5小游戏在介绍小游戏之前,先看一个框架 Phaser。Phaser 框架是一个 快速、免费且开源的 HTML5 游戏开发框架,它提供了 Canvas 和 WebGL 两种渲染方式,兼容 PC 端与移动端浏览器。一、Phaser 版本在启动 Phaser 游戏前,需要定义一个 Phaser.Game 对象实例,并同时将配置信息传至该实例:var game = Phaser.Game(...

逻辑研究_cjz_huateng的博客-程序员秘密

1.你让工人为你工作7天,回报是一根金条,这个金条平分成相连的7段,你必须在每天结束的时候给他们一段金条。如果只允许你两次把金条弄断,你如何给你的工人付费?2.村子中有50个人,每人有一条狗。在这50条狗中有病狗(这种病不会传染)。于是人们就要找出病狗。每个人可以观察其他的49条狗,以判断它们是否生病,只有自己的狗不能看。观察后得到的结果不得交流,也不能通知病狗的主人。主人一旦推算出自己家的是

大型网站架构之分布式消息队列_公众号-老炮说Java的博客-程序员秘密

21套精品Java架构师高并发高性能高可用分布式集群教程(4000G)39阶段精品云计算大数据项目实战视频教程200本经典编程相关书籍下载互联网技术(java框架分布式集群)干货视频大全...

随便推点

SLAM学习(一)_vins failure detection!_乌龟抓水母的博客-程序员秘密

VINS学习1.代码框架。。。。2.细节(1)故障检测(failure detection)存在于VINS_ESTIMATOR的estimator.cpp中有failureDetection()根据shaojie shen的讲座,他提到故障检测的有这么一些场景:(1)当前帧的特征点太少(2)非线性求解器中的解有大跳动(3)相机IMU的外参矩阵或IMU偏移(bias)求解出大跳动...

1条命令解决不能完成此操作,因为项目“Karabiner-Elements”已被锁定_代码写注释的博客-程序员秘密

不能完成此操作,因为项目“Karabiner-Elements”已被锁定。sudo "/Library/Application Support/org.pqrs/Karabiner-Elements/uninstall.sh"

Debian中安装zlib-devel_debian zlib_one312的博客-程序员秘密

在Debian软件源里zlib和zlib-devel是zlib1g zlib1g.devapt-get install zlib1gapt-get install zlib1g.dev若不能安装。就需要先安装装ruby 然后再装zlib1g-devapt-get install zlib1g-dev...

【mathematica画三维空间坐标系箭头】_mathematica坐标轴箭头_超级大白的博客-程序员秘密

先看图代码(符号什么的都是英文状态下)Graphics3D[{{Thick, Red, Arrowheads[.03], Arrow[{{0, 0, 0}, {2, 0, 0}}]}, {Thick, Green, Arrowheads[.03], Arrow[{{0, 0, 0}, {0, 2, 0}}]}, {Thick, Blue, Arrowheads[.03], ...

Matplotlib入门系列6_关于Matplotlib中文乱码这回事_simei字体_hyh123a的博客-程序员秘密

2019年7月15日14:34:34环境Python3.6前言Matplotlib的一些基础知识1、创建2、简单的plt例子3、绘图样式的修改4、一次创建多个子图5、保存图片6、关于Matplotlib中文乱码这回事直接上代码操作,可以体验一下,自己根据自己需要修改即可。关于实际效果的生成,自己实际运行一下就会显示,截图还是有点麻烦的,就不截图了。建议刚开始读一...

ubuntu自动挂载ISCSI硬盘_小冰_童鞋的博客-程序员秘密

一、安装iSCSI客户端apt-getinstall open-iscsi二、修改iscsid.conf配置文件(这样就能开机启动!)vim/etc/iscsi/iscsid.conf设置node.starup为automatic:node.startup = automatic三、查找iSCSItargets主机的targetnameiscsiadm --

推荐文章

热门文章

相关标签