effective C++ 笔记:条款11 在operator=

2018-08-16  本文已影响0人  jun_hinokeso

直接上代码~

class Widget{...};
Widget w;
w = w;

上述代码中有一个自我赋值的操作,这种自我赋值非常明显,但是有些自我赋值就不一定那么明显了,比如

a[i] = a[j]; // i 和 j 值相等

*px = *py;  // px py 指向相同

class base{...};
class derived : public base{...};
void doSomething(const base& rb, derived* pd);  //实际上,rb 和pd可能指向同一对象

那我们接着来看一下对象的自我赋值可能会发生什么事情。
下面是一个类内的operator=实现代码

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

此时我们假设传入的rhs和=左边的(也就是*this)是同一个东西,那么在new pb的时候,其实rhs.pb其实已经被delete!,那么此时return的 *this,其实里面的pb指向的是一个已经被删除的Bitmap对象。
对于这种情况,有一种简便方法就是在这段代码前面加一个证同测试(identity test):

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

这样的代码看似没有问题,但是我们还少考虑了“异常状况”,比如

    pb = new Bitmap(*rhs.pb);

在这段分配空间的操作中如果导致了异常(内存不足或者Bitmap的构造函数抛出异常),这时候返回的*this,其中的pb仍然是指向一个已经被删除的Bitmap。
鉴于此有了新的改进代码

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

这样,在new操作之前就没有delete操作,就不会产生指向已删除对象的指针,此时就算报出异常,pb还是原来的值,并不会那么地危险。而且就算传入的rhs和*this是同一个东西,这段代码就相当于复制了一个和原来一样的Bitmap给pb。
以上思想还有两种写法:

Widget& Widget::operator=(const Widget& rhs){
    Widget tmp(rhs);
    swap(tmp);   
    return *this;
}
Widget& Widget::operator=(const Widget rhs){ //这里不是引用  是值传递
    swap(tmp);   
    return *this;
}

总结:确保对象如果出现自我赋值时不会有不良的行为,并且确定任何函数如果操作一个以上的对象,而且这些对象是同一个对象时,也不会出现不良的行为。

上一篇下一篇

猜你喜欢

热点阅读