C++ Exceptional 写异常安全的代码

本文介绍了C++异常安全编程的三个层次,并通过分析new、delete操作符的异常安全性和示例代码,强调了异常安全的重要性。讨论了如何在拷贝构造函数、赋值操作、Stack的Pop实现中确保异常安全,以及使用placement new和placement delete来管理资源,避免内存泄露。异常安全的实现方式包括:将可能抛出异常的代码封装、职责分离以及利用Temp-Swap策略。
摘要由CSDN通过智能技术生成

C++ Exceptional中有一大段的章节是写异常安全的,这可能也是很多程序员在平时的编程中所容易忽视的一个部分,这里我大体总结一下这本书中所涉及到的有关异常安全的知识。

异常安全有三种层次:
1. 基本的异常安全: 当异常发生时,保证没有内存泄露
2. 强的异常安全:当异常发生时,不但要保证没有内存泄露,还要保证程序的状态和异常发生前是一致的。
3. 异常永不发生的保证: 即保证在任何情况下,异常都不会发生。

在写代码时,尽量保证两个原则:
1. 异常安全,实现以上三种异常安全层次之一。
2. 异常中立(exception neutral),当有异常发生时,捕获后要重新抛出,让调用者知道。

我们讨论一下C++中new操作符和delete操作符的异常安全问题。
new操作符本身是异常安全的,为什么这样说呢,因为如果在new一个或多个对象的时候,一般会发生两种类型的错误。第一种是bad_alloc异常,即没有申请到内存,因为这个时候连内存都没申请到,所以发生了异常,也不会有内存泄露。另外一种情况是,对象的构造发生了异常,这个时候此对象会构造失败,并且已经构造好的对象也会被析构掉,所占用的内存也会被释放掉。所以说new操作符是异常安全的。
对已delete操作符来说,情况就要复杂一些了。在delete一个数组对象时,若其中一个对象发生了异常,将导致在这个对象之后的对象无法被正确析构,从而发生内存泄露。所以说这个就要求我们所提供的类的析构函数是不能抛出异常的。

把有可能产生异常的代码移到另一个函数中。只有当另一个函数里的事情成功做完后,你才可以用不可能抛出异常的操作改变程序的状态
代码示例如下:

template<class T>
class Stack
{
public:
    Stack();
    ~Stack();
    Stack(const Stack&);
    Stack& operator=(const Stack&);
    /*...*/
private:
    T* v_;           //ptr to  a memery area big
    size_t vsize_;   //enough for 'vsize_' T's
    size_t vused_;   //of T's acctually in use
}

template<class T>
T* NewCopy( const T* src,
            size_t srcsize,
            size_t destsize)
{
    assert(destsize >= srcsize);
    T* dest = new T[destsize];
    try
    {
        copy(src, src+srcsize, dest);
    }
    catch(...)
    {
        delete [] dest; // this can't throw
        throw;          // rethrow original exception, 
                        // exeption neutral pinciple
    }
    return dest;
}

//copy construction
template<class T>
Stack<T>::Stack(const Stack<T>& other)
:v_(NewCopy(other.v_,
            other.vsize_,
           
exceptional c++:47个c++工程难题、编程问题和解决方案(中文版)》讲述如何用标准c++进行企业级的软件开发,通过“问题/解答”的方式,启发读者思考,帮助了解隐藏在问题背后的设计思想,以及各种编程指导原则适用的场合。本书列出的条款涵盖了许多方面的主题,尤其对异常安全性、类和模块的合理设计,正确的代码优化,以及编符合c++标准的可移植代码进行了深入的讨论。   《exceptional c++:47个c++工程难题、编程问题和解决方案(中文版)》适于有一定c++编程基础的读者阅读。 目录 《exceptional c++:47个c++工程难题、编程问题和解决方案(中文版)》 1 泛型程序设计与c++标准库 1 条款1:迭代器难度系数 1 条款2:大小不敏感的字符串——之一 5 条款3:大小不敏感的字符串——之二 9 条款4:可重用性最高的泛型容器——之一 12 条款5:可重用性最高的泛型容器——之二 13 条款6:临时对象 22 条款7:标准库的使用(或者,再论临时对象) 28 2 异常安全性相关的问题与技术 31 条款8:编异常安全代码——之一 32 条款9:编异常安全代码——之二 37 条款10:编异常安全代码——之三 40 条款11:编异常安全代码——之四 47 条款12:编异常安全代码——之五 50 条款13:编异常安全代码——之六 56 条款14:编异常安全代码——之七 62 条款15:编异常安全代码——之八 65 条款16:编异常安全代码——之九 68 条款17:编异常安全代码——之十 73 条款18:代码的复杂性——之一 75 条款19:代码的复杂性——之二 79 3 类的设计与继承 85 条款20:类的编技巧 85 条款21:虚函数的重载 93 条款22:类之间的关系——之一 99 条款23:类之间的关系——之二 103 条款24:继承的使用和滥用 110 条款25:面向对象程序设计 121 4 编译器防火墙和pimpl惯用法 123 条款26:将编译期依赖性降到最低——之一 123 条款27:将编译期依赖性降到最低——之二 127 条款28:将编译期依赖性降到最低——之三 132 条款29:编译防火墙 135 条款30:fast pimpl惯用法 138 5 名字查找、名字空间和接口规则 148 条款31:名字查找与接口规则——之一 148 条款32:名字查找与接口规则——之二 152 条款33:名字查找和接口规则——之三 162 条款34:名字查找与接口规则——之四 167 6 内存管理 176 条款35:内存管理——之一 176 条款36:内存管理——之二 179 条款37:auto_ptr 186 7 误区、陷阱以及错误的惯用法 201 条款38:对象标识 201 条款39:自动转换 204 条款40:对象的生存期——之一 206 条款41:对象的生存期——之二 209 8 其他主题 219 条款42:变量的初始化 219 条款43:正确使用const 222 条款44:类型转换 231 条款45:bool 238 条款46:转调函数 242 条款47:控制流程 244 后记 254 参考书目 256
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值