• C++ 自赋值与异常安全问题


    https://harttle.land/2015/07/30/effective-cpp-11.html,这个讲的很不错!

    1.存在的问题

    因为cpp中有指针和引用,它们可以指向同一个变量,所以会存在自赋值的问题。

    a[i] = a[j];     //@ 如果i和j有同样的值,这里就是一次自赋值
    *px = *py;        //@ 如果px和py指向相同的东西,这里就是一次自赋值 

    自赋值一般存在:自赋值安全和异常安全,两个问题,例如:

    1.1自赋值安全

    Widget& Widget::operator=(const Widget& rhs){
        delete pb;                   // stop using current bitmap
        pb = new Bitmap(*rhs.pb);    // start using a copy of rhs's bitmap
        return *this;                // see Item 10
    }

    当是自赋值的时候,pb已经先被删除了,那么后面的new就会为空,这是未知的计算。

    1.2 异常安全

    异常安全是指当异常发生时:

    • 1) 不会泄漏资源
    • 2) 也不会使系统处于不一致的状态。

     通常有三个异常安全级别:基本保证、强烈保证、不抛异常(nothrow)保证。

    Widget& Widget::operator=(const Widget& rhs){
        if (this == &rhs) return *this;
        delete pb;                   // stop using current bitmap
        pb = new Bitmap(*rhs.pb);    // start using a copy of rhs's bitmap
        return *this;                // see Item 10
    }

    这个自赋值安全,但是没有异常安全,如果new处出现了异常,那么pb仍旧指向空。

    2.解决办法

    2.1 通过调整语句顺序

    Widget& Widget::operator=(const Widget& rhs){
        Bitmap *pOrig = pb;               // remember original pb
        pb = new Bitmap(*rhs.pb);         // make pb point to a copy of *pb
        delete pOrig;                     // delete the original pb
        return *this;
    }

    这里用pO指向原来的动态空间,在new时如果失败了抛出bad alloc异常,那么pb仍然指向原来的空间,不会悬空。

    通过一个中间变量来实现,而没有使用if的判断,因为可能会影响效率。

    其实也可以这样:

        HasPtr & operator=(const HasPtr& hp){
            std::string * t=new std::string(*hp.ps);
            delete ps;
            ps=t;
            i=hp.i;
            return *this;
        }

    主要就是申请一个临时变量来指向原来的空间或者是新申请的空间。//delete应该是不会出现异常的吧。

    2.2 copy&swap

    Widget& Widget::operator=(Widget rhs){
        swap(rhs);                // swap *this's data with
        return *this;             // the copy's
    }

    注意参数为值拷贝,在cpp459页有讲解,

     总结:

    1. 判断两个地址是否相同
    2. 仔细地排列语句顺序
    3. Copy and Swap

    自赋值存在的知识点差不多这些。

    3.不在自赋值中的异常安全问题

    转自:https://harttle.land/2015/08/27/effective-cpp-29.html

    但其实异常安全问题不仅仅存在于自赋值中,上述连接中给的例子,在更新数据成员时,也可能会出现异常安全的问题:

    void Menu::changeBg(istream& src){
        lock(&mutex);
        delete bg;
        ++ changeCount;
        bg = new Image(src);
        unlock(&mutex);
    }

    当要更新bg时,new出现了问题,则mutex资源泄露,bg变为空悬指针。

    针对这个问题,当然这个和自赋值问题是不同的,可以将数据成员bg用智能指针来管理,也可以使用copy&swap技术.

    3.1 智能指针smart_ptr

    class Menu{
        shared_ptr<Image> bg;
        ...
    };
    void Menu::changeBg(istream& src){
        Lock m1(&m);
        bg.reset(new Image(src));
        ++changeCont;
    }

    reset函数中会delete掉原来的空间,但是如果new失败了,并不会进入reset函数,不会对bg产生影响。(但也有问题,但之后的叙述不明白。)

    3.2 copy&swap

    class Menu{
        ...
    private:
        Mutex m;
        std::shared_ptr<MenuImpl> pImpl;
    };
    Menu::changeBg(std::istream& src){
        using std::swap;            // 见 Item 25
        Lock m1(&mutex);
    
        std::shared_ptr<MenuImpl> copy(new MenuImpl(*pImpl));
        copy->bg.reset(new Image(src));
        ++copy->changeCount;
    
        swap(pImpl, copy);
    }

    先对原来的对象做一个拷贝,然后修改拷贝对象,再swap,函数结束时,copy对象及其指向的内存会被释放,不会造成泄露。

  • 相关阅读:
    利用ItextPdf、core-renderer-R8 来生成PDF
    把war包放到Tomcat安装文件夹下,不能直接訪问的解决方式
    我的RTOS 之六 -- Touch移植(s5pv210+threadx+ucgui+touch)
    数据库可用率监控工具
    9款极具创意的HTML5/CSS3进度条动画(免积分下载)
    ODBC与JDBC比較
    ORA-02287: 此处不同意序号
    mongodb创建、更新、删除
    jcenter那些事儿
    C#里的应用程序域AppDomain
  • 原文地址:https://www.cnblogs.com/BlueBlueSea/p/14018994.html
Copyright © 2020-2023  润新知