2012-03-26 16 views
1

Effective C++(Scott Meyers)を見ている間に、データメンバーをあるオブジェクトから別のオブジェクトにコピーするときに例外を処理する方法を示すために、 。コピー代入演算子の例外処理(C++)

class Bitmap { ... }; 

class Widget { 
    ... 

private: 
    Bitmap *pb;          // ptr to a heap-allocated object 
}; 

Widget& Widget::operator=(const Widget& rhs) 
{ 
    Bitmap *pOrig = pb;    // remember original pb 
    pb = new Bitmap(*rhs.pb);   // make pb point to a copy of *pb 
    delete pOrig;      // delete the original pb 
    return *this; 
} 

"新しいビットマップ"が例外をスローすると、pbは変更されません。ただし、 pOrigを削除すると、pbが指すメモリが解放されます。これは危険ではないですか?それはどのように「新しいビットマップ」は、例外を生成するときので悪いです(彼は主張して)次のコード

Widget& Widget::operator=(const Widget& rhs) 

{ 
    if (this == &rhs) return *this; // identity test: if a self-assignment, 
            // do nothing 
    delete pb; 
    pb = new Bitmap(*rhs.pb); 
    return *this; 
} 

(ビットマップのコピーコンストラクタは1を投げますので、そこに割り当てるための十分なメモリがあるかのどちらかので)よりも良くありウィジェットは、削除されたビットマップへのポインタを保持することになります

私は本の正誤表をチェックしましたが、この例は言及されていません。私は明白な何かを欠いていますかまた、誰かがこの例外を処理するより良い方法を提案できますか?

答えて

2

が成功した場合にのみ、delete pOrig;が実行されます。割り当てが失敗した場合、このctorはまったく実行されません。代わりに、スタックが解放され、コンストラクタの任意の部分から例外がスローされたハンドラに直接実行されます。途中で停止するのは、ctorのローカル変数を破棄することだけですが、唯一のローカル変数はポインタなので、それを破壊するのはかなりノーです。

ウィジェットオブジェクトに他のメンバー変数が含まれている場合、完全に構築されたものもスタックの巻き戻しの一部として破棄されますが、ここでは無関係な(表示されていない) 。

+0

ありがとうございました。 – Sam

0

例外安全性の主な原則は、例外をスローする可能性のある操作の前に、記録データを変更したくないということです。

deleteがレコードのデータを変更し、その後にスローする可能性のあるnew演算子が続き、データが不完全な状態になるため、2番目の例ではこれを渡しません。

new操作がスローされるまでデータのどれもが変更されないため、最初の例にはこの問題はありません。データが不完全な状態になる可能性はありません。

0

"新しいビットマップ"が例外をスローすると、pbは変更されずに のままです。ただし、pOrigを削除すると、pbが指すメモリ が解放されます。これは危険ではないですか?

いいえ、削除されるpOrigについて誤った仮定がありました。例外がスローされる可能性があります。元のコードでは:ここoperator newを呼び出す

Widget& Widget::operator=(const Widget& rhs) 
{ 
    Bitmap *pOrig = pb; // <-- this can't throw 
    pb = new Bitmap(*rhs.pb); // <-- this can throw 
    delete pOrig; // <-- this can't throw 
    return *this; // <-- this can't throw 
} 

は、コードを投げるかもしれない唯一の場所です。そうであれば、pbには結果が割り当てられません。以前のビットマップを指し、クラスは有効な状態のままです。また、指摘先pOrigも削除されません。その結果、例外がスローされた場合、リークはなく、クラスは有効な状態のままです。

あなたのコードでは例外ではありません。

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

あなたはpbに関連付けられたメモリを解放したら、無効な状態でクラスを置きます。したがって、クラスを有効な状態に戻すまで投げるのは危険です。 operator newがここにスローされた場合、あなたは悩まされており、pbがぶら下がりポインタであるため、あなたのウィジェットクラスは無効な状態のままです。 2行目をまったく実行していないかのようになります。

自分自身に好意を持ち、心を救う。 RAIIとスマートポインタを使用すると、ずっと簡単になります。

Widget& Widget::operator=(const Widget& rhs) 
{ 
    unique_ptr<Bitmap> new_bitmap(new Bitmap(*rhs.pb)); 
    pb.swap(new_bitmap); // make pb a unique_ptr as well 
    return *this; 
} 
関連する問題