http://blog.csdn.net/bonchoix/article/details/8046727

一个函数如果说是“异常安全”的,必须同时满足以下两个条件:1.不泄漏任何资源;2.不允许破坏数据。 我们先通过两个反面的例子开始。

第一个是造成资源泄漏的例子。一个类Type,内含一个互斥锁成员 Mutex mutex,以及一个成员函数void Func()。假设Func函数的实现如下所示:

  1. void Type::Func()
  2. {
  3. Lock(&mutex);
  4. DoSomething();
  5. UnLock(&mutex);
  6. }

首先是获得互斥锁,中间是做该做的事,最后释放互斥锁。从功能上来讲很完整,没任何问题。但从异常安全角度来说,它却不满足条件。因为一旦DoSomething()函数内部导致异常,UnLock(&mutex)将不会被执行,于是互斥器将永远不会被释放了。换句话说即造成了资源泄漏。

再来看第二个造成数据破坏的例子。这个例子是我们很熟悉的重载 ‘=’ 操作符的成员函数。依然假设一个类Type,其中一个成员是一个指向一块资源(假设类型为T)的指针。这时候我们一般就需要来自定义复制构造函数和重载复制操作符以及析构函数了。(绝大多数情况下,这三个成员总是要么同时存在,要么都不用定义,因为编译器默认定义了,即C++中所谓的 ”Rule of 3" 规则。这里不作详细介绍)。这里我们只考虑重载复制操作符的问题,其部分代码假设如下:

  1. class Type
  2. {
  3. public:
  4. ....
  5. Type& operator = (const Type &t)
  6. {
  7. if(this == &t)
  8. return *this;
  9. else
  10. {
  11. delete m_t;
  12. m_t = new T(t->m_t);
  13. return *this;
  14. }
  15. }
  16. ....
  17. private:
  18. T *m_t;
  19. };

首先来判断是否是自我复制,如果是,则直接返回自己。如果不是,则安全释放当前指向的资源,再创建一块与被复制的对象资源一模一样的资源并指向它,最后返回复制好的对象。同样,抛开异常安全来看,没问题。但是考虑到异常安全性时,一旦“new T(t->m_t)"时抛出异常,m_t将指向一块已被删除的资源,并没有真正指向一块与被复制的对象一样的资源。也就是说,原对象的数据遭到破坏。

C++中’异常安全函数”提供了三种安全等级:

1. 基本承诺:如果异常被抛出,对象内的任何成员仍然能保持有效状态,没有数据的破坏及资源泄漏。但对象的现实状态是不可估计的,即不一定是调用前的状态,但至少保证符合对象正常的要求。

2. 强烈保证:如果异常被抛出,对象的状态保持不变。即如果调用成功,则完全成功;如果调用失败,则对象依然是调用前的状态。

3. 不抛异常保证:函数承诺不会抛出任何异常。一般内置类型的所有操作都有不抛异常的保证。

如果一个函数不能提供上述保证之一,则不具备异常安全性。

现在我们来一个个解决上面两个问题。

对于资源泄漏问题,解决方法很容易,即用对象来管理资源。RAII技术之前介绍过,这里就不再赘述。我们在函数中不直接对互斥锁mutex进行操作,而是用到一个管理互斥锁的对象MutexLock ml。函数的新实现如下:

  1. void Type::Func()
  2. {
  3. MutexLock ml(&mutex);
  4. DoSomething();
  5. }

对象ml初始化后,自动对mutex上锁,然后做该做的事。最后我们不用负责释放互斥锁,因为ml的析构函数自动为我们释放了。这样,即时DoSomething()中抛出异常,ml也总是要析构的,就不用担心互斥锁不被正常释放的问题了。

对于第二个问题,一个经典的策略叫“copy and swap"。原则很简单:即先对原对象做出一个副本(copy),在副本上做必要的修改。如果出现任何异常,原对象依然能保证不变。如果修改成功,则通过不抛出任何异常的swap函数将副本和原对象进行交换(swap)。函数的新实现如下:

  1. Type& Type::operator = (const Type &t)
  2. {
  3. Type tmp(t);
  4. swap(m_t,tmp->m_t);
  5. return *this;
  6. }

先创建一个被复制对象t的副本tmp,此时原对象尚未有任何修改,这样即使申请资源时有异常抛出,也不会影响到原对象。如果创建成功,则通过swap函数对临时对象的资源和原对象资源进行交换,标准库的swap函数承诺不抛出异常的,这样原对象将成功变成对象 t 的复制版本。对于这个函数,我们可以认为它是”强烈保证“异常安全的。

当然,提供强烈保证并不是总是能够实现的。一个函数能够提供的异常安全性等级,也取决于它的实现。考虑以下例子:

  1. void Func()
  2. {
  3. f1();
  4. f2();
  5. }

如果f1和f2都提供了”强烈保证“,则显然Func函数是具有”强烈保证“的安全等级。但是如果f1或f2中有一个不能提供,则Func函数将不再具备”强烈保证“等级,而是取决于f1和f2中安全等级最低的那个。

总结:

为了让代码具有更好的异常安全性,首先是”用对象来管理资源“,以避免资源的泄漏。其次,在异常安全性等级上,应该尽可能地往更高的等级上来限制。通过 copy-and-swap 方法往往可以实现”强烈保证“。但是我们也应该知道,”强烈保证“并不是对所有的情况都可实现,这取决于你在实现中用到的函数。函数提供的异常安全性的最高等级只能是你实现中调用的各个函数中异常安全性等级最低的那个。

参考:《Effective C++》,第三版。

C++中的异常安全性的更多相关文章

  1. C++中的异常安全性【转】

    原文写的非常好,来自这里 一个函数如果说是“异常安全”的,必须同时满足以下两个条件:1.不泄漏任何资源:2.不允许破坏数据. 我们先通过两个反面的例子开始. 第一个是造成资源泄漏的例子.一个类Type ...

  2. Java中的异常简介

    Java中异常的分类 Java中的异常机制是针对正常运行程序的一个必要补充,一般来说没有加入异常机制,程序也能正常运营,但是,由于入参.程序逻辑的严谨度,总会有期望之外的结果生成,因此加入异常机制的补 ...

  3. C++ 中的异常机制分析

    C++异常机制概述 异常处理是C++的一项语言机制,用于在程序中处理异常事件.异常事件在C++中表示为异常对象.异常事件发生时,程序使用throw关键字抛出异常表达式,抛出点称为异常出现点,由操作系统 ...

  4. PHP中Exception异常

    异常的基本使用 当异常被抛出时,其后的代码不会继续执行,PHP 会尝试查找匹配的 "catch" 代码块. 如果异常没有被捕获,而且又没用使用 set_exception_hand ...

  5. 【Java心得总结二】浅谈Java中的异常

    作为一个面向对象编程的程序员对于 下面的一句一定非常熟悉: try { // 代码块 } catch(Exception e) { // 异常处理 } finally { // 清理工作 } 就是面向 ...

  6. python虚拟机中的异常流控制

    异常:对程序运行中的非正常情况进行抽象.并且提供相应的语法结构和语义元素,使得程序员能够通过这些语法结构和语义元素来方便地描述异常发生时的行为. 1.Python中的异常机制: 1.1Python虚拟 ...

  7. Python中获取异常(Exception)信息

    异常信息的获取对于程序的调试非常重要,可以有助于快速定位有错误程序语句的位置.下面介绍几种python中获取异常信息的方法,这里获取异常(Exception)信息采用try...except...程序 ...

  8. Java中测试异常的多种方式

    使用JUnit来测试Java代码中的异常有很多种方式,你知道几种? 给定这样一个class. Person.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ...

  9. Atitit 数据处理查询 中的异常标准化草案 jpa jdbc hb  oql规范attilax总结

    Atitit 数据处理查询 中的异常标准化草案 jpa jdbc hb  oql规范attilax总结 Javaee6 与net 异常规范1 Jpa规范 JPA全称Java Persistence A ...

随机推荐

  1. 三种Hash算法对比以及秒传原理.

    三种Hash算法对比以及秒传原理 CRC (32/64)   MD5  Sha1 分5个点来说 1.校验值长度 2.校验值类别 3.安全级别 4.应用场景 1).校验值长度 CRC(32/64) 分别 ...

  2. 洛谷P1275 魔板

    P1275 魔板 题目描述 有这样一种魔板:它是一个长方形的面板,被划分成n行m列的n*m个方格.每个方格内有一个小灯泡,灯泡的状态有两种(亮或暗).我们可以通过若干操作使魔板从一个状态改变为另一个状 ...

  3. 解读人:李思奇,Development of a sensitive, scalable method for spatial, cell-type-resolved proteomics of the human brain. (一种用于研究人类大脑基于空间或细胞类型的蛋白质组学的灵敏方法)

    发表时间:(2019年4月) 一. 概述: 本文报道了一种可研究人类大脑组织中特定神经细胞的蛋白质组学的方法.作者通过激光捕获显微切割技术(LCM)从逝者大脑中分离出目的神经元细胞,接着尝试了一系列不 ...

  4. Gradle用户使用指南

    转载请事先沟通,未经允许,谢绝转载. 1. 新工具介绍(Introduction) 能够复用代码和资源能够构建几种不同版本参数的应用能够配置.扩展.自定义构建过程1.1 为什么选择Gradle(Why ...

  5. PostGIS安装 pgis3.4.2 postgresql 10.1

    https://yq.aliyun.com/articles/228258http://download.osgeo.org/postgis/source/http://blog.51cto.com/ ...

  6. springBoot2.0 配置@ControllerAdvice 捕获异常统一处理

    一.前言 基于上一篇 springBoot2.0 配置shiro实现权限管理 这一篇配置 异常统一处理 二.新建文件夹:common,param 三.返回结果集对象 1.ResultData.java ...

  7. HTTP的学习记录(二)头部

    本文主要讲一些 HTTP头部的信息 首先看一段 惊为天人 的文章. 来自于 <淘宝技术这十年> 你发现快要过年了,于是想给你的女朋友买一件毛衣,你打开了www.taobao.com.这时你 ...

  8. 华东交通大学2017年ACM“双基”程序设计竞赛 1008

    Problem Description 长度为 n 的序列,把它划分成两段非空的子序列,定义权值为:两段子序列的最大值的差的绝对值.求可能的最大的权值.数据范围:2 <= n <= 10^ ...

  9. Codeforces Round #129 (Div. 2) C

    Description The Little Elephant very much loves sums on intervals. This time he has a pair of intege ...

  10. Win10创建还原点

    Win + E打开任务管理器 右击此电脑,选择属性 单击高级系统设置 选择系统保护 单击系统还原 按照提示操作,OK.