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. servlet之doPost()、doGet()

    1.doGet和doPost方法的具体应用?即在什么时候程序调用doGet方法,什么时候程序执行doPost方法? HttpServlet是从GenericServlet继承而来,因此HttpServ ...

  2. 2017-10-26 NOIP模拟赛2

    财富 (treasure) Time Limit:1000ms   Memory Limit:128MB 题目描述 LYK有n个小伙伴.每个小伙伴有一个身高hi. 这个游戏是这样的,LYK生活的环境是 ...

  3. bzoj4200: [Noi2015]小园丁与老司机(可行流+dp)

    传送门 这该死的码农题…… 题解在这儿->这里 //minamoto #include<iostream> #include<cstdio> #include<cs ...

  4. JDK动态代理和cglib代理

    写一个简单的测试用例,Pig实现了Shout接口 public class MyInvocation implements InvocationHandler { Object k; public M ...

  5. DNS解析工具--nslookup和dig使用

    1.nslookup使用 [root@master ~]# nslookup> server 8.8.8.8    #指定域名服务器Default server: 8.8.8.8Address: ...

  6. php数组处理函数

    array_reverse()数组反向排序,$arr=array_reverse($arr)

  7. 打造H5动感影集的爱恨情仇–动画性能篇

    “你听说过动感影集么?” 动感影集是QQ空间新功能,可以将静态的图片轻松转变为动态的视频集,且载体是HTML5(简称H5)页面,意味着可以随时分享到空间或朋友圈给好友欣赏! 移动端区别于PC年代的相册 ...

  8. URAL 1948 H - The Robot on the Line 二分 + 数学

    http://acm.hust.edu.cn/vjudge/contest/126149#problem/H 给定一条二次函数 f (x) = a * x * x + b * x + c 求一个最小的 ...

  9. Java多线程与并发——生产者与消费者应用案例

    多线程的开发中有一个最经典的操作案例,就是生产者-消费者,生产者不断生产产品,消费者不断取走产品. package com.vince; /** * 生产者与消费者案例 * @author Admin ...

  10. CAST 变类型,如 SELECT CAST('12' AS int)

    select distinct CAST(value as signed) valueprice,CAST(value as signed) valueid from app_coupon