effective C++ 条款 11:在operator= 处理‘自我赋值’

假设建立一个class来保存一个指针指向一块儿动态分配的位图(bitmap)

class Bitmap{...};
class Widget
{
public:
protected:
private:
    Bitmap* pb;
};

这个operator=实现代码表面看上来合理, 但自我赋值出现时并不安全,也不具备异常安全性。

Widget& Widget::operator=(const Widget& rhs)
{
    delete pb;
    pb = new Bitmap(*rhs.pb);
    return *this;
}

rhs和*this可能是同一个对象。这样的话delete就不只是销毁当前对象的bitmap,它也销毁rhs的bitmap。

预阻止这种错误,传统的做法是在operator=的最前面的一个“证同测试”identity test

Widget& Widget::operator=(const Widget& rhs)
{//证同测试,如果是自我赋值就不做任何事
    if (this == &rhs)
    {
        return *this;
    }

    delete pb;
    pb = new Bitmap(*rhs.pb);
    return *this;
}

还不具备“异常安全性”,如果“new Bitmap”导致异常(不论是因为分配时内存不足,还是因为Bitmap的copy构造函数抛出异常),Widget最终会有一个指针指向一快被删除的Bitmap。

让operator=具备“异常安全性”往往自动获得“自我赋值安全”的回报。如下代码,只需注意在复制pb所指东西之前别删除pb

 

Widget& Widget::operator=(const Widget& rhs)
{
    Bitmap* pOrig = pb;
    pb = new Bitmap(*rhs.pb);
    delete pOrig;
    return *this;
}

这样如果new Bitmap抛出异常,pb 保持原状。即使没有证同测试,还是能够处理自我赋值,只是不是最高效的办法,但他行得通。如果关心效率, 把“证同测试”加上,但是这项测试本身也需要成本,是代码变大,并导入一个新的控制流分支,而且“自我赋值”的发生频率并不高。

确保代码不但“异常安全”而且“自我赋值安全”的一个替代方案是,使用所谓的copy and swap技术:

class Widget
{
public:
    void swap(Widget& rhs); //交换*this和rhs的数据
protected:
private:
    Bitmap* pb;
};

Widget& Widget::operator=(const Widget& rhs)
{
    Widget temp(rhs);
    swap(temp);
    return *this;
}

这个主题的另一个变奏曲:1.copy assignment操作符可能被声明为“以by value方式接受实参”;2.以by value方式传递东西会造成一份副本:

Widget& Widget::operator=(Widget rhs) //pass by value
{
    swap(rhs);
    return *this;
}

确保当对象自我赋值时operator=有良好行为。其中技术包括比较“来源对象”和“目标对象”的地址、精心周到的语句顺序、以及

copy and swap

确定任何函数如果操作一个以上的对象,而其中有多个对象是同一个时,其行为仍然正确

原文链接: https://www.cnblogs.com/lidan/archive/2012/01/13/2322060.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    effective C++ 条款 11:在operator= 处理‘自我赋值’

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/40125

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月8日 下午4:49
下一篇 2023年2月8日 下午4:50

相关推荐