假设建立一个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】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/40125
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!