14
//code from https://skillsmatter.com/skillscasts/2188-move-semanticsperfect-forwarding-and-rvalue-references
class Widget {
public:
    Widget(Widget&& rhs)
        : pds(rhs.pds) // take source’s value
    { 
        rhs.pds = nullptr;  // why??
    }

private:
    struct DataStructure;
    DataStructure *pds;
};

rhd.pdsに設定する理由がわかりませんnullptr

この行を削除するとどうなりますか :rhs.pds = nullptr;

4

3 に答える 3

15

クラスの一部の詳細が削除されました。特に、コンストラクタはDataStructureオブジェクトを動的に割り当て、デストラクタはその割り当てを解除します。移動中にポインターを一方から他方にコピーした場合Widget、両方Widgetの は同じ割り当てられたDataStructureオブジェクトへのポインターを持ちます。次に、それらのオブジェクトが破壊されると、両方が試みdeleteます。これにより、未定義の動作が発生します。これを回避するために、Widget移動元の には、 に設定する内部ポインタがありますnullptr

これは、ムーブ コンストラクターを実装する際の標準的なパターンです。動的に割り当てられたオブジェクトの所有権をあるオブジェクトから別のオブジェクトに移動したいので、元のオブジェクトがそれらの割り当てられたオブジェクトを所有していないことを確認する必要があります。

DataStructureダイアグラム的には、次の状況から始めて、 の所有権を一方から他方に移動したいと考えてWidgetいます。

    ┌────────┐        ┌────────┐
    │ Widget │        │ Widget │
    └───╂────┘        └────────┘
        ┃
        ▼
 ┌───────────────┐
 │ DataStructure │
 └───────────────┘

ポインターをコピーしただけの場合は、次のようになります。

    ┌────────┐        ┌────────┐
    │ Widget │        │ Widget │
    └───╂────┘        └───╂────┘
        ┗━━━━━━━━┳━━━━━━━┛
                  ▼
         ┌───────────────┐
         │ DataStructure │
         └───────────────┘

その後、元のWidgetポインターをnullptrに設定すると、次のようになります。

    ┌────────┐         ┌────────┐
    │ Widget │         │ Widget │
    └────────┘         └───╂────┘
                           ┃
                           ▼
                  ┌───────────────┐
                  │ DataStructure │
                  └───────────────┘

所有権が正常に転送され、Widget未定義の動作を引き起こすことなく両方の を破棄できる場合。

于 2014-03-01T11:58:24.823 に答える
2

DataStructureオブジェクトは によって「所有」されている可能性が高く、ポインタをリセットすると、が破棄されたときにオブジェクトがWidget誤って削除されるのを防ぐことができます。Widget

別の方法として、オブジェクトが移動元になったときにオブジェクトを「空」または「デフォルト」の状態にリセットするのが慣習的であり、ポインターをリセットすることは慣例に従う無害な方法です。

于 2014-03-01T11:57:40.647 に答える
1
class Widget {
  public:
    Widget(Widget&& rhs)
       : pds(rhs.pds) // take source’s value
    { 
        rhs.pds = nullptr;  // why??
    }
    ~Widget() {delete pds}; // <== added this line

private:
    struct DataStructure;
    DataStructure *pds;
};

上記のクラスにデストラクタを追加しました。

Widget make_widget() {
    Widget a;
    // Do some stuff with it
    return std::move(a);
}

int main {
    Widget b = make_widget;
    return 0;
}

nullptr 割り当てを削除するとどうなるかを説明するために、上記の方法を確認してください。ウィジェット a はヘルパー関数で作成され、ウィジェット b に割り当てられます。

ウィジェット a がスコープ外になると、そのデストラクタが呼び出され、メモリの割り当てが解除され、無効なメモリ アドレスを指しているウィジェット b が残ります。

nullptr を rhs に割り当てると、デストラクタも呼び出されますが、delete nullptr は何も実行しないため、すべて問題ありません:)

于 2014-03-01T12:11:27.457 に答える