effective C++メモ:条項11 operator=で「自己付与」を処理

1933 ワード

直接コードをつける~
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のコンストラクタ放出異常)が発生した場合、pbは削除されたBitmapを指す*thisを返します.これにより、新しい改良コードが得られます.
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にコピーすることに相当します.以上の考えには2つの書き方があります.
  • Widget& Widget::operator=(const Widget& rhs){
        Widget tmp(rhs);
        swap(tmp);   
        return *this;
    }
    
  • Widget& Widget::operator=(const Widget rhs){ //            
        swap(tmp);   
        return *this;
    }
    

    まとめ:オブジェクトが自己付与された場合に不良な動作をしないことを確認し、任意の関数が1つ以上のオブジェクトを操作し、これらのオブジェクトが同じオブジェクトである場合にも不良な動作をしないことを決定します.