异常安全的代码是指,满足两个条件

1异常中立性 :
是指当你的代码(包括你调用的代码)引发异常时,这个异常 能保持原样传递到外层调用代码
2.异常安全性:

 1,抛出异常后,资源不泄露,
2,抛出异常后,不会使原有数据恶化(例如正常指针变野指针)
3。。少些try catch,因为大量的try catch会影响代码逻辑。导致代码丑陋混乱不优雅
一段代码要具有异常安全性,必须同时具有异常中立性和一定等级的异常安全性保证
异常安全的等级一般有:
1,函数提供基本保证(the basic guarantee)(不会发生内存泄漏并且程序内的每个对象都处在合法的状态,没有流错位,没有野指针,但是不是每个对象的精确状态是可以预期的,如可能发生异常后,指针处在空指针状态,或者某种默认状态,但是客户无法预知到底是哪一个),对于达成基本保证可以多使用智能指针来管理资源
2,函数提供强力保证(the strong guarantee),强力保证含义是,成功或者回滚保证,发生异常的函数对程序来说,没有任何改动,提供发生异常时候的回滚机制。
调用提供强力保证的函数之后,仅有两种可能的程序状态:像预期一样成功执行了函数,或者函数回滚继续保持函数被调用时当时的状态。与之相比,如果调用只提供基本保证的函数引发了异常,程序可能存在于任何合法的状态。
函数提供强力保证的有效解决办法是
copy-and-swap:
先做出一个你要改变的对象的copy,然后在这个copy上做出全部所需的改变。如果改变过程中的某些操作抛出了异常,最初的对象保持不变。在所有的改变完全成功之后,将被改变的对象和最初的对象在一个不会抛出异常的操作中进行swap。
3. 函数有不抛出保证(the nothrow guarantee),对于所有对内建类型(例如,ints,指针,等等)的操作都是不抛出(nothrow)的(也就是说,提供不抛出保证)。这是异常安全代码中必不可少的基础构件。
注意事项:
异常安全 最关键的是:swap ctor dctor 不发生异常保证,只有成功或者终止程序两种状态
一个函数的异常安全等级,是取决于它所调用的函数中最低异常安全等级的函数。
C++11新增了noexcept关键字,在void func() noexcept{}noexcept保证了这个函数不会抛出异常,只有终止程序和成功执行两种状态。
noexcept可以接受一个常量表达式,noexcept(constexpr。。。)当常量表达式为转换为true说明该函数保证不抛出异常。
 
从异常安全的观点看,不抛出的函数(nothrow functions)是极好的,但是在 C++ 的 C 部分之外部不调用可能抛出异常的函数简直就是寸步难行。使用动态分配内存的任何东西(例如,所有的 STL 容器)如果不能找到足够的内存来满足一个请求,在典型情况下,它就会抛出一个 bad_alloc 异常。只要你能做到就提供不抛出保证,但是对于大多数函数,选择是在基本的保证和强力的保证之间的。
 
但是,不是所有函数都能做出异常保证的,考虑这样一个函数,函数内部的函数内是一个对数据库的操作,一旦异常发生,难以撤销对数据库的更改。如果想对这样的函数做到异常的strong guarantee保证,就是非常困难度事情。
所以对于只对局部变量改变的函数保证异常安起会相对比较容易。如果函数的操作中牵扯到全局变量等等,就变得困难的多。
解决异常安全的好办法:
1,多使用RAII,使用智能指针来管理内存。由于unwind机制的保证,当异常发生时,函数栈内已构造的局部对象的析构函数会被一一调用,在析构函数内释放资源,也就杜绝了内存泄漏的问题。
2,做好程序设计。特别是异常发生时的回滚机制的正确使用,copy-and-swap是有效的方法。
3,注意需要异常保证的函数内部的调用函数,异常安全等级是以有最低等级异常保证的函数确定的。
一个系统即使只有一个函数不是异常安全的,那么系统作为一个整体就不是异常安全的,因为调用那个函数可能发生泄漏资源和恶化数据结构。
4,对于一些需要保证性能的程序,在提供基本的异常安全时,要注意,栈解退机制只是调用析构函数,对于内置类型的操作不会被回滚,所以。像起累加器作用的一些内置类型变量,应该注意在函数成功执行后再进行累加。避免数据结构恶化。重新分配资源给原本已经持有资源的变量,应该先清空释放变量的资源,指针再设置为nullptr,防止资源重新分配过程中抛出异常,导致指针变为野指针的问题。
5,流对象,资源对象,new对象,不应该直接作为参数,一旦抛出异常,就可能会导致严重的问题,函数也许会被错误的执行,资源也许会泄漏。对于函数参数和函数内使用的全局变量,应该保证在进入函数内部是是正常状态。
6.减少全局变量的使用,对包含全局变量的函数做异常安全是比较困难的事情,栈解退也只对局部变量起效果。
7,如果不知道如何处理异常,就不要捕获异常,直接终止比吞掉异常不处理要好
8.保证 构造 析构 swap不会失败
 
这里有个注意事项:
在构造函数中,如果抛出异常,是不会调用当前正在构造的类的析构函数的,因为当前正在构造的类没有构造完成,只会析构已经构造完成成员和父类,So,极易导致内存泄漏,这里要谨慎处理,使用RAII,智能指针,noexcept保证不会抛出异常和恶化数据。
 
参考文章:
1:对象生死劫-构造函数和析构函数异常
2:C++箴言:争取异常安全的代码
3:如何编写异常安全的代码

C++异常安全的思考的更多相关文章

  1. 关于java异常的一点思考

    关于异常的一点思考 异常生命周期 异常的来源 所有的异常都是抛出来的 有底层api抛出的 有自定义抛出的 异常的处理 1, 运行时异常 不做任何处理仍可编译通过 不建议捕获(不建议用异常来做流程控制, ...

  2. 【高并发】由InterruptedException异常引发的思考

    写在前面 InterruptedException异常可能没你想的那么简单! 前言 当我们在调用Java对象的wait()方法或者线程的sleep()方法时,需要捕获并处理InterruptedExc ...

  3. 由DBCursor的“can't switch cursor access methods”异常引发的思考

    先谈谈我是怎么用的: DBCollection dbcollection = XXXXXXXXXX(); //连接mongo DBCursor dbCursor = mergeVideoDB.find ...

  4. 集合并发修改异常-foreach的时候不可修改值

    直接上代码: 无意间发现的://这个方法本身是为后面的集合去掉前面集合的重复数据一直报错,并发修改异常,仔细看mainList正在迭代循环,然后我进行了remove操作,这个时候就会报这个错.故:总结 ...

  5. Interrupted Exception异常可能没你想的那么简单!

    摘要: 当我们在调用Java对象的wait()方法或者线程的sleep()方法时,需要捕获并处理InterruptedException异常.如果我们对InterruptedException异常处理 ...

  6. 201453408刘昊阳 《Java程序设计》第5周学习总结

    201453408刘昊阳 <Java程序设计>第5周学习总结 教材学习内容总结 第8章 异常处理 8.1 语法与继承结构 8.1.1 使用try.catch p227代码(Average) ...

  7. Python中定时任务框架APScheduler

    前言 大家应该都知道在编程语言中,定时任务是常用的一种调度形式,在Python中也涌现了非常多的调度模块,本文将简要介绍APScheduler的基本使用方法. 一.APScheduler介绍 APSc ...

  8. 为什么iterator,foreach遍历时不能进行remove操作?除了一种情况可以这样(特殊情况)?

    Exception in thread "main" java.util.ConcurrentModificationException 并发修改异常引发的思考! 1 foreac ...

  9. JDK7 新特性

    JDK7新特性的目录导航: 二进制字面值 switch 语句支持 String try-with-resources catch 多个类型异常 字面值中使用下划线 类型推断 改进泛型类型可变参数 其它 ...

随机推荐

  1. Python常用的标准库以及第三方库有哪些?

    20个必不可少的Python库也是基本的第三方库 读者您好.今天我将介绍20个属于我常用工具的Python库,我相信你看完之后也会觉得离不开它们.他们是: Requests.Kenneth Reitz ...

  2. XAML 绑定和结构体不得不说的问题

    遇见一个问题 如果用一个结构体struct.再用一个ListView,然后使用绑定. <Window x:Class="WpfApp1.MainWindow" xmlns=& ...

  3. m3m4加载器的优化版m3m4-v1.1

    m3m4加载器的优化版m3m4-v1.1 /* //1.以$开头的模块名,表示服务,服务只会执行一次,它可以有自己的方法.比如cookie就是一个服务 //例如:console.log("r ...

  4. Python入门(2)

    变量补充 一:变量名的名称的大前提:应该能够反映出变量值所记录的状态 具体的,变量名命名规范如下: 1.变量名是由字母.数字.下划线组成 2.不能以数字开头 3.不能使用关键字命名变量名['and', ...

  5. MyBatis 源码解析SqlSession.getConnection(Connection异常:关闭或者不在Mybatis事务)

    从SqlSession的实现类SqlSessionTemplate源码中,看出相关方法被其内部类SqlSessionInterceptor(实现了InvocationHandler)代理 当调用Sql ...

  6. 解决nginx文件服务器访问403

    2018-10-24 nginx配置文件目录服务器 修改/etc/nginx/conf.d/default.conf或者在/etc/nginx/conf.d/目录下添加一配置文件,如下 server ...

  7. vector与array之间转换,向量与数据之间转换

    一维数组: vector<int> a; int b[5] = {1,2,3,4,5}; a.push_back(b);   二维数组: b[5][6] = {1,2,3,4,5,6... ...

  8. python 爬虫系列08-同步斗图一波

    一波大图来袭 import requests from lxml import etree from urllib import request import os import re def par ...

  9. 最近准备把安卓和java的知识再回顾一遍,顺便会写博客上!千变万化还都是源于基础,打扎实基础

    最近准备把安卓和java的知识再回顾一遍,顺便会写博客上!千变万化还都是源于基础,打扎实基础,加油吧 距离去北京还有23天

  10. 虚拟机xp系统中Oracle 10g的安装

    1 安装过程(11步) 2.如果是xp系统可以直接并双击解压目录下的setup.ext,出现安装界面,如下: 3.输入口令和确认口令,如:oracle,点击下一步,出现如下进度条. 注:此口令即是管理 ...