2017-11-26 14 views
-3

私は移動のセマンティクスで少し苦労しています。私はその話題について多くのことを読んでいますが、答えが見つからなかった2つの具体的な問題があり、それをあなたに提示するのが好きです。存在しないオブジェクトの破壊

#include <iostream> 
#include <cstddef> 

class A 
{ 
    public: 
     A *ptr; 
     virtual ~A() 
     { 
      std::cout << "dtor" << std::endl; 
      delete ptr; 
      ptr=nullptr; 
     } 
}; 

main() 
{ 
    A x, y; 
    x.ptr = &y; 
} 
// compile and link like this with g++: g++ -std=c++0x -lstdc++ code.cc 

クラスAは、自身のメンバーを有することができる:

まず、私は、以下の例示的なコードを有しています。私はこの「複合パターン」の概念を使って階層を構築しています。しかし、この場合、オブジェクトxが破棄されると、そのデストラクタはオブジェクトyへのポインタを削除します。最後にオブジェクトyを破壊する必要がありますが、すでにエラーが発生しています...どうすればこの問題を解決できますか?

後者の場合は、次のようになります。

main() 
{ 
    A x, y; 
    y = std::move(x); 
    std::cout << "x.ptr: " << x.ptr << std::endl; 
    std::cout << "y.ptr: " << y.ptr << std::endl; 
} 

をここで、私は両方の参照は、両方が存在することを意味する同じであることがわかります。私はstd :: moveを使ってコンテンツをxからyに移動します。私はx.ptrが "空"であると思っていたでしょうか?

ありがとうございます!最初のケースで

乾杯

+3

あなたは1つの 'delete'と' new'sを持っています。 – chris

+0

テストすると、x.ptrとy.ptrの両方がnull値として取得されました。また、私は2番目のエラーを取得しませんでした。 –

+1

2番目のケースに対処する: 'std :: move'はあまりそれ自体をしません、データの*移動*は移動コンストラクタによって行われます。デフォルトの移動コンストラクタはメンバワイズ移動を行いますが、 (ポインタのように)単なるコピーです。したがって、 'x.ptr'を" empty "に設定しません。 – vu1p3n0x

答えて

1

、彼らは自動的に破壊されますので、あなたのxとyの両方のオブジェクトがスタック上に作成されます。しかし、クラスAのデストラクタでは、deleteが使用されます。これは、ptrによって参照されるオブジェクトが新しいものでヒープ上に作成されたものとみなします。 したがって、スタック上に作成されたオブジェクトを参照するようにx.ptrを設定するとエラーが発生します(yなど)。正しいコードは、これは、生のポインタの危険性である

x.ptr = new A; 

だろう - あなたはオブジェクトを動的に割り当てられるために、ポインタを区別することはできません(あなたが持っていることを最終的にdeleteをする)と「ただのポインタ」(あなたができないこと)。

ローポインタA*の代わりにstd::unique_ptr<A>を使用するとよいでしょう。 それは私自身の削除とも呼ばれるので、delete ptrは必要ありません。

2番目のケースでは、プリミティブ型の場合、実際にはmoveは単なるコピーです。 intからint、またはptrからptrに移動した場合は、プリミティブ型の移動では最適化するものがないためコピーされているだけなので、移動元のオブジェクトを特別に「空にする」必要はありません。しかし、あなたのケースでは、スマートポインタと呼ばれる良いものがあります。これは実際に "空にする"ことです(正確さを保証するためです)。あなたはそれを推測しました - 私はstd::unique_ptrについて話しています。

また、ptrをA*からstd::unique_ptr<A>に変更すると、探している仕事が移動します。

ここでは、std::unique_ptrという2つの単語があなたの本当の友達です。その後部材x.ptrにYのアドレスを割り当てる最初に、スタックにタイプAの2つのオブジェクトを作成

A x,y; 

0

。オブジェクトがスコープから外れると、そのデストラクタが呼び出されます。それはあなたのケースで

y.~A() 
x.~A() 

する必要がありますので、これは手動でデストラクタで削除を呼び出し、これはすでに削除されたオブジェクトに削除呼び出します、その構造の逆の順序で発生したときに実行x.~A()

これではない、あなたのコード内の唯一の問題:deletes

  • You call delete on an object on the stack.コールがnewへの呼び出しによって一致する必要があります。さらに便利には、newまたはdeleteを手動で呼び出し、std::unique_ptrとstd :: shared_ptr`を使用する必要があります。
  • bのデストラクタが呼び出されると、delete ptrはptrに初期化されていない値を使用します。コンパイラ生成コンストラクタは、ptr tp nullptrを初期化しません。私は、デバッグモードでこれをリリースモードで行う少なくとも1つのコンパイラを知っています。

std::moveを使用すると、最後に移動コンストラクタが呼び出されます(またはassignmnet演算子が移動されます)。手動で定義するのではなく、デストラクタを定義するので、コンパイラ生成の移動コンストラクタはありません。したがって、あなたの場合、最終的に暗黙のコピーコンストラクタが呼び出され、メンバの値がコピーされます。これはあなたが見ているものです。

あなたはコンパイラが生成した移動のセマンティクスを要求する

class A 
{ 
    public: 
     A(A&& a) = default; 
     A& operator=(A&& a) = default; 

     A *ptr; 
     virtual ~A() 
     { 
      std::cout << "dtor" << std::endl; 
      delete ptr; 
      ptr=nullptr; 
     } 
}; 

を追加した場合でも、それはdoes a member-wise moveは、ポインタ型の場合

A(A&& a): ptr(std::move(a.ptr)) {} 

のように見える、これはちょうどに値を代入するコードを生成します。新しい変数を読み込み、古い変数をそのままにします。移動したオブジェクトに対する標準の要件は非常に基本的であり、これによって実現されます。

関連する問題