Effective C++T 11:operator=での自己付与処理


Effective C++学習ノート総リンク
プログラムと設計を改善する55の具体的な方法学習ノート-毎日1条
条項11:operator=での自己付与の処理
【テクニック】
1.オブジェクトが自己割り当てされたときにoperator=が良好に動作していることを確認します.ここで、技術としては、「ソースオブジェクト」と「ターゲットオブジェクト」のアドレスの比較、周到な文の順序、copy-and-swapがある.
2.任意の関数が1つ以上のオブジェクトを操作し、複数のオブジェクトが同じオブジェクトである場合でも、その動作が正しいことを決定します.
自己付与セキュリティの問題
別名:あるオブジェクトを指す方法が1つ以上あります.
一般的に、pointerとreferenceが「複数の同じタイプのオブジェクトを指す」ために使用されるセグメントコードが操作されている場合は、これらのオブジェクトが同じかどうかを考慮する必要があります.
実際には、2つのオブジェクトは同じ継承システムから来ている限り、同じタイプとして宣言する必要もないため、base classのreferenceまたはpointerがderived classを指す可能性があるため、別名の原因となります.
class Base{
      ... };
class Derived: public Base{
      ... };
void doSomething(const Base& rb, Derived* pd); //rb *pd        

【結果】:自己付与を処理していない場合、リソースを自分で管理しようとすると、「リソースの使用を停止する前に予期せぬ解放」の罠に落ちる可能性があります.
次のコードを見てください.自己付与は安全ではありません.
class Bitmap{
      ... };
class Widget
{
     
	...
private:
	Bitmap* pb; //   ,     heap     
}

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

上記のコードがもたらす問題:operator=関数内の*this(付与の宛先)とrhsは同じオブジェクトである可能性があります.このときdeleteが破棄するのは,現在のオブジェクトのBitmapだけでなくrhsのBitmapも破棄する.関数の最後に、Widget内に削除されたオブジェクトを指すポインタがあります.
自己付与の解決策
自己付与の証明書とテスト
operator=関数の先頭に「証明書テスト」を追加し、「自己付与」の検査目的を達成します.
コードは次のとおりです.
Widget& Widget::operator=(const Widget& rhs)
{
     
	if (this == &rhs)  //     :
		return *this;  //        ,     *this
	delete pb; 
	pb = new Bitmap(*rhs.pb); 
	return *this;
}

しかし、上記のコードの方法は依然として異常な面で面倒である.「new Bitmap」によって例外が発生した場合(割り当て時にメモリが不足しているか、Bitmapのcopyコンストラクション関数によって例外が放出されているかにかかわらず)、Widgetは最終的に削除されたBitmapを指すポインタを持っています.
このようなポインタは非常に有害で、安全に削除することができず、安全に読み取ることもできません.それらにできる唯一の安全なことは、多くのデバッグエネルギーを払って誤った起源を見つけることです.
自己付与の異常なセキュリティ
pbが指すものをコピーする前にpbを削除しないでください.
より良い自己付与方法
Widget& Widget::operator=(const Widget& rhs)
{
     
	Bitmap* pOrig = pb; //      pb
	pb = new Bitmap(*rhs.pb); //  pb  *pb     
	delete pOrig; //      pb
	return *this;
}

現在、「new Bitmap」が異常を投げ出すと、pbは元のままになります.
このコードは、元のBitmapをコピーし、新しく製造されたレプリカを指し、元のBitmapを削除するため、証明されたテストがなくても、自己付与を処理することができます.「自己付与」を処理する最も効率的な方法ではないかもしれませんが、それは通じます.
効率に関心がある場合は、「自己付与」の発生頻度を事前に推定し、状況に応じて「証明テスト」を関数の先頭に置くことができます.
なぜなら、同じテストでは、元のコードとターゲットコードを含むコードが大きくなり、新しい制御ストリームブランチがインポートされ、実行速度が低下するからです.
copy and swap技術
代替案は、同様に実行可能であり、copy and swap技術は、異常な安全性と密接に関連している(条項29)
class Widget
{
     
	void swap(Widget& rhs); //   *this   rhs    
};

Widget& Widget::operator=(const Widget& rhs)
{
     
	Widget temp(rhs); //  rhs        
	swap(temp); //  *this              
	return *this;
}

このテーマのもう一つの変奏曲
Widget& Widget::operator=(Widget rhs)
{
     
	swap(rhs); //  *this              
	return *this;
}
  • あるclassのcopy assignmentオペレータは、「by value方式で実パラメータを受け入れる」
  • と宣言される可能性があります.
  • by valueで物を伝えるとコピーが作成されます(条項20)
  • Scott Meyersは、賢いために明瞭性を巧みに犠牲にしたと考えている.
    「copyingアクション」を関数本体から「関数パラメータ構築フェーズ」に移動すると、コンパイラがより効率的なコードを生成する場合があります.