原文写的非常好,来自这里

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

总结:

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

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

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

  1. C++中的异常安全性

    http://blog.csdn.net/bonchoix/article/details/8046727 一个函数如果说是“异常安全”的,必须同时满足以下两个条件:1.不泄漏任何资源:2.不允许破坏 ...

  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. CI框架 -- 核心文件 之 Exceptions.php

    使用CI框架,我们通常使用一下三个函数处理错误: show_error('消息' [, int $status_code = 500 ] ) show_404('页面' [, 'log_error'] ...

  2. nodejs基础 -- web模块

    什么是 Web 服务器? Web服务器一般指网站服务器,是指驻留于因特网上某种类型计算机的程序,Web服务器的基本功能就是提供Web信息浏览服务.它只需支持HTTP协议.HTML文档格式及URL,与客 ...

  3. js获取网页的url文件名( 例如index.aspx),js获取url的参数(例如获取 ?cid=joeylee的值),给jquery拓展方法

      <script type="text/javascript"> var JsRequest={ //这就是一个静态类,类里面有2个静态方法 //方法一:获取url的 ...

  4. VMware克隆虚拟机后无法启动网卡

    最简单的办法: 修改这个文件:/etc/udev/rules.d/70_persistent-net.rules 把eth1修改为eth0,记下里面的mac地址 修改/etc/sysconfig/ne ...

  5. C# 分享几个ip的方法吧,包括正则验证ip

    /// <summary> /// 正规则试验IP地址 /// </summary> /// <param name="IP"></par ...

  6. Intellij 2016非服务器激活

    下载 http://idea.lanyus.com/jar/JetbrainsCrack-2.6.2.jar 并将 JetbrainsCrack-2.6.0.jar 放置到 D盘根目录(也可以根据自己 ...

  7. Linux系统下wetty安装和使用说明

    1. Wetty简介 Wetty是使用Node.js和websockets开发的一个开源Web-based SSH.关于Web-based SSH的更多资料请参考https://en.wikipedi ...

  8. 内存管理 初始化(五)kmem_cache_init 初始化slab分配器(上)

    看了下kmem_cache_init,涉及到不同MIGRATE间的buddy system的迁移,kmem_cache的构建,slab分配器头的构建.buddy system的伙伴拆分. 对于SMP系 ...

  9. windows mongodb最常用命令简单归纳

    在windows安装好了windows,首先记得要把mongodb bin目录路径放在 系统环境变量的path中,确定之后即配置好了mongo的环境变量,在dos命令框中输入mongo会出现如下 版本 ...

  10. 在 ubuntu 【6.06、6.10】 上安装 oracle 10.2.0.1,并打补丁 10.2.0.5

    特点: ubuntu 6.06.6.10 算是很古老的ubuntu了,其应该是基于 debian 4 的 tesing/unstable 分支.所以,毛病较多. 如何安装oracle10g? 几个技术 ...