zoukankan      html  css  js  c++  java
  • Effective C++:条款29:为“异常安全”而努力是值得的

    (一)先看以下这些代码:

    class PrettyMenu {
    public:
    	void changeBackground(istream& imgSrc);
    private:
    	Mutex mutex;          //由于这个class希望用于多线程环境,所以它有这个相互排斥器作为并发控制之用
    	Image* bgImage;       //眼下的背景图像
    	int imageChanges;     //背景图像被改变的次数
    };
    
    void PrettyMenu::changeBackground(std::istream &imgSrc) {
    	lock(&mutex);                    //取得相互排斥器
    	delete bgImage;                  //摆脱旧的背景图像
    	++imageChanges;                  //改动图像变更次数
    	bgImage = new Image(imgSrc);     //安装新的背景图像
    	unlock(&mutex);                  //释放相互排斥器
    }
    上面的函数不是“异常安全性”函数,由于“异常安全性”函数所需满足的两个条件。上面这个函数都不满足。

    第一个条件:不泄露不论什么资源。

    (上面这个函数当new Image(imgSrc)发生异常的话。unlock(&mutex)就永远不会被运行,所以就资源泄漏了)。

    第二个条件:不同意数据败坏。(上面这个函数当new Image(imgSrc)发生异常的话,bgImage就是指向一个已删除的对象)。


    (二)

    (1)解决上面那个函数泄漏资源的问题的办法:用Lock class作为一种“确保相互排斥器被及时释放”的方法,以对象管理资源

    void PrettyMenu::changeBackground(std::istream &imgSrc) {
    	Lock m1(&mutex);                  
    	delete bgImage;                  //摆脱旧的背景图像
    	++imageChanges;                  //改动图像变更次数
    	bgImage = new Image(imgSrc);     //安装新的背景图像
    }

    (2)解决上面那个函数数据败坏的办法

    首先,

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

    1.  基本承诺:假设异常被抛出,程序内的不论什么事物仍然保持在有效的状态下。
    2.  强烈保证:假设异常被抛出,程序状态不改变。
    3.  不抛掷保证:承诺绝不抛出异常,由于它们总是可以完毕它们原先承诺的功能。

    对changeBackground而言,首先。从一个类型为Image*的内置指针改为一个“用于资源管理”的智能指针。第二,又一次排列changeBackground内的语句次序。使得在更换图像之后再累加imageChanges。

    又一次排列语句次序

    class PrettyMenu { 
        ... 
        std::tr1::shared_ptr<Image> bgImage; 
        ... 
    };
    
    void PrettyMenu::changeBackground(std::istream& imgSrc) { 
        Lock ml(&mutex); 
        bgImage.reset(new Image(imgSrc)); 
        ++imageChanges; 
    }
    这两个改变差点儿足够让changeBackground提供强烈的异常安全保证。美中不足的是參数imgSrc。

    假设Image构造函数抛出异常。有可能输入流的读取记号已被移走。而这种搬移对程序其余部分是一种可见的状态改变。


    (三)

    copy and swap的原则:为打算改动的对象做一个副本,在那个副本上做一切必要改动。若有不论什么改动动作抛出异常,源对象仍然保持未改变状态。待全部改变都成功后,再将改动过的副本和原对象在一个不抛出异常的swap中置换。

    实际上一般是将全部“隶属对象的数据”从原对象放进还有一个对象内,然后赋予原对象一个指针,指向那个所谓的实现对象(即副本)。
    实现例如以下:使用copy and swap方法下的pimpl idiom手段:
    struct PMImpl { 
        std::tr1::shared_ptr<Image> bgImage; 
        int imageChanges; 
    }; 
    class PrettyMenu {  
    private: 
        Mutex mutex; 
        std::tr1::shared_ptr<PMImpl> pImpl; 
    }; 
    void PrettyMenu::changeBackground(std::istream& imgSrc) { 
        using std::swap; 
        Lock ml(&mutex); 
        std::tr1::shared_ptr<PMImpl> pNew(new PMImpl(*pImpl)); 
        pNew->bgImage.reset(new Image(imgSrc)); //改动副本 
        ++pNew->imageChanges; 
        swap(pImpl, pNew);//置换数据 
    }



    请记住:

    (1)异常安全函数(Exception-safe functions)即使发生异常也不会泄露资源或同意不论什么数据结构败坏。

    这种函数区分为三种可能的保证:基本型、强烈型、不抛异常型。

    (2)“强烈保证”往往可以以copy-and-swap实现出来,但“强烈保证”并不是对全部函数都可实现或具备现实意义。

    (3)函数提供的“异常安全保证”通常最高仅仅等于其所调用的各个函数的“异常安全保证”中的最弱者。











  • 相关阅读:
    C#生成图形验证码
    飞刀软件官网正式开通
    IIS7或者IIS7.5部署MVC项目时出现404错误
    office编程必不可少 [转]
    C# 利用 HttpWebRequest 和 HttpWebResponse 模拟登录有验证码的网站
    console方便用法
    24个解决实际问题的ES6代码段
    遍历对象的属性和对象的值
    前端图片处理
    Vue团队代码规范
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/5367648.html
Copyright © 2011-2022 走看看