Effective C++(11) 自我赋值(a=a)时会发生什么?

来源:互联网 发布:python np.split axis 编辑:程序博客网 时间:2024/06/05 08:08
问题聚焦:
自我赋值看似有点愚蠢的行为,其实总会发生的
首先:它是合法的,
其次,它不一定是安全的,
再次,它有时候不是那么明显。

先看一个Demo

class Widget { ... };Widget w;... /** 最明显的自我赋值 **/w = w;   /** 不那么明显的自我赋值 **/// 在某个地方实现了i = j或者相同作用的事情a[i] = a[j]/** 潜在的自我赋值 **/*px = *py;/** 更为隐蔽的自我赋值“别名” **/class Base { ... };class Derived : public Base { ... };void doSomething( const Base& rb, Derived* pd);     // rb 和 *pd可能是同一对象


一般而言,如果某段代码操作指针和引用,而它们被用来指向多个相同类型的对象,就需要考虑这些对象是否为同一个了。

自我赋值可能带来的两种潜在的危险:
  • 自我赋值安全性
  • 异常安全
来看下面的例子:

class Bitmap { ... };class Widget {    ... private:    Bitmap* pd;};/** operator=的代码实现 **/Widget&Widget::operator=(const Widget& rhs){    delete pb;    pb = new Bitmap(*rhs.pb);    return *this;}

自我赋值安全性:
若rhs和当前对象是同一对象,那么在销毁当前对象的pb时,把rhs的对象也销毁了。

改进:认同测试


/** operator=的代码实现 **/Widget&Widget::operator=(const Widget& rhs){    if (this == &rhs) return *this;   // 认同测试    delete pb;    pb = new Bitmap(*rhs.pb);    return *this;}


上述改进后的代码避免了自我赋值安全性问题。
我们来关注一下第二个问题
异常安全性:
如果new Bitmap()发生异常,那么结果就是pb赋值直白,Widget最终持有一个指针指向一块被删除的Bitmap。

改进一:精心安排语句


/** operator=的代码实现 **/Widget&Widget::operator=(const Widget& rhs){    Bitmap* pOrig  = pb;    pb = new Bitmap(*rhs.pb);     // 如果new Bitmap抛出异常,pb保持原状    delete pOrig;    return *this;}


改进方案二:copy and swap


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

思想:
拷贝构造函数要声明为“按值传递”
按值传递会造成一份副本


小结:
确保当对象自我赋值时operator=有良好的行为。
包括:
对象来源
目标对象地址
语句顺序
确定任何函数如果操作一个以上的对象,而其中多个对象是同一个对象时,其行为仍然正确。



参考资料:
《Effective C++ 3rd》

0 0
原创粉丝点击