美文网首页
effective C++ 笔记:条款11 在operator=

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

作者: jun_hinokeso | 来源:发表于2018-08-16 22:13 被阅读0次

    直接上代码~

    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;
    }
    

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

    相关文章

      网友评论

          本文标题:effective C++ 笔记:条款11 在operator=

          本文链接:https://www.haomeiwen.com/subject/fehabftx.html