zoukankan      html  css  js  c++  java
  • C++:编写异常安全代码

    在C++的使用当中,最令人头疼的地方莫非是内存管理或者异常的使用。

    想写出一个真正异常安全的代码是非常难得,需要考虑的因素有非常多。

    在现代C++当中也有很多人提倡不使用异常,但是要完全杜绝使用C++异常

    也是很难的,除非打算不使用任何一个标准库,重写所有需要用的数据结构算法等等。

    在一般情况下,适当的使用标准库也能提高程序的开发效率,和健壮性。

    毕竟标准库已经被广泛使用,代码质量高,但是使用异常会导致代码的膨胀。

    为此,我们只能保证最少的使用异常,但是编写异常安全的函数是必不可少的。

    void MyClass::foo() {
        lock(&mutex);
        delete buffer;
        ++counter;
        buffer = new CBuffer();
        unlock(&mutex);
    }

    从“异常安全性”的观点来看,这个函数很糟。“异常安全”有两个条件,而这个函数没有满足其中的任何一个条件

    当异常被抛出时,带有异常安全性的函数会:

    • 不泄露任何资源。上述代码没有做到,因为一旦new CBuffer()导致异常,unlock就永远不会调用,于是就产生了死锁。
    • 不允许数据破坏。如果new CBuffer()导致异常,buffer就指向了一个已经被删除的对象,counter也已经被累加。

    使用RAII保证在出现异常时正确的释放资源

    void MyClass::foo() {
        Lock lock(&mutex);
        delete buffer;
        ++counter;
        buffer = new CBuffer();
    }

    目前死锁的问题被解决了,但是数据破坏的问题还没有被解决。

    此刻我们需要做一些抉择,在抉择之前我们必须先面对一些用来定义选项的术语。

    异常安全函数提供一下三个保证之一:

    • 基本承诺:如果异常被抛出,程序内的任何事物仍然保证在有效状态下。
    • 强烈保证:如果异常被抛出,程序状态不改变,拥有原子性,要么全部成功,要么回到调用函数前的状态。
    • 不抛掷(nothrow)保证:承诺绝对不抛出异常。

    我们可以采用一种策略。这个策略被称为:copy and swap。

    原则很简单,打算为所有要改变的对象做出一份副本,然后在副本上面进行修改,

    待所有副本修改完毕,再将副本与原对象在一个不抛出异常的操作中置换出来(swap)。

    struct PMImpl {
        std::shared_ptr<CBuffer> buffer;
        int    counter;
    };
    
    void MyClass::foo() {
        Lock lock(&mutex);
        // 使用基于RAII的指针管理方式
        std::shared_ptr<PMImpl> new_impl(new PMImpl );
        new_impl->buffer.reset(new CBuffer);
        new_impl->counter = pimpl->counter + 1;
        // 使用不抛出异常的swap
        swap(pimpl, new_impl);
    }

    “copy and swap”策略是对对象状态做出”全有或者全无“改变的一个很好的办法。

    一个软件系统要么就具备异常安全性,要不就全然否定,没有所谓的”局部异常安全系统“。

    如果系统内有一个函数不具备异常安全性,整个系统就不具备异常安全性。

    四十年前,满载goto的代码被视为一种美好实践,而今我们却致力写出结构化控制流。二十年前,全局数据被视为一种美好实践,而今我们却致力于数据的封装。十年前,写“未将异常考虑在内”的函数被视为一种美好实践,而今我们致力写出“异常安全码”。时间不断前进,我们与时俱进。

  • 相关阅读:
    缩放图片
    Volley下载图片存放在data/data下 networkImageView lrucache
    类实现Parcelable接口在Intent中传递
    基本控件设置边角图片 drawableleft
    屏幕全屏之类的问题
    关于点击按钮分享
    万能适配器的一些问题
    自定义控件高级
    Fragment 生命周期 全局变量的声明位置
    GridView
  • 原文地址:https://www.cnblogs.com/windpiaoxue/p/10012123.html
Copyright © 2011-2022 走看看