2

スタック変数の代入を上書きする前にデストラクタが呼び出されたかどうかを確認するテストを作成しましたが、結果の合理的な説明が見つかりません...

これは私のテストです (Visual C++ 2008 リリース モードで):

#include <iostream>
class C {
public:
 char* ptr;
 C(char p) { ptr = new char[100]; ptr[0] = p;}
 ~C() { std::cout << ptr[0] << ' '; delete [] ptr; }
};

int _tmain(int argc, _TCHAR* argv[])
{
 {
  C s('a');
  s = C('b');
  s = C('c');
  s = C('d');
 }
 std::cin.get();
 return 0;
}

仮説が正しければ「abcd」、偽であれば「d」を期待していました。代わりに「bcdx」が表示されます。「x」は、ランダムなヒープ値を読み取っていることを示す ptr に割り当てられているメモリの量に応じて変化します。

何が起こっていると思いますか(間違っている場合は修正してください)、各コンストラクター呼び出しが新しいスタック値を作成し(それらをs1、s2、s3、s4と呼びましょう)、割り当てによってs1.ptrがs4.ptrによって上書きされたままになります. s4 はコピーの直後に破棄されますが、s1 (ダングリング ptr を含む) はスコープを離れると破棄され、s4.ptr が二重に削除され、元の s1.ptr は削除されません。

shared_ptrs の使用を伴わない、この役に立たない動作を回避する方法はありますか?

編集: 「削除」を「削除 []」に置き換え

4

7 に答える 7

11

三つのルール

前述のように、複数のオブジェクトが共通のポインタへのアクセスを共有し、それを読み取ろうとするため、アプリケーションの動作は未定義です...

3つのルールでは、次のいずれかを定義するたびに次のいずれかを定義します。

  • コピーコンストラクタ
  • 代入演算子
  • デストラクタ

次に、もう一方を定義する必要があります。これは、オブジェクトには、デフォルトで生成されたメソッドが認識しない特定の動作があるためです。

特別な例外の編集:
デストラクタを仮想化するため、または属性の特別な処理があるためではなく、何かをログに記録するためにのみ定義する場合があります;)

于 2009-10-07T12:26:03.187 に答える
3

デストラクタで出力するため、インスタンスはスコープの最後(表示されているx)で削除されます。

他のインスタンスは、割り当てが行われるとすぐに削除されます。それはbcdxを説明します。

次に使用する

delete [] ptr; 

削除する代わりに

于 2009-10-07T12:26:04.920 に答える
2

その他のコンパイラ定義メソッドを追加します。

class C
{
    public:
      char* ptr;
      C(char p)                { ptr = new char[100]; ptr[0] = p;}
     ~C()                      { std::cout << ptr[0] << ' '; delete [] ptr; }
      C(C const& c)            { ptr = new char[100]; ptr[0] = c.ptr[0];}
      C& operator=(C const& c) { ptr[0] = c.ptr[0]; return *this;}
};

int _tmain(int argc, _TCHAR* argv[])
{
  {
      C s('a');
      s = C('b');
      s = C('c');
      s = C('d');
  }
  std::cin.get();
  return 0;
}

次のように出力されます。

bcdd

各テンポラリは、式の最後で破棄されます。次に、s が最後に破棄されます (「d」が ptr[0] にコピーされた後)。各メソッドに print ステートメントを貼り付けると、何が起こっているかを簡単に確認できます。

>>           C s('a');
Construct 'a'

>>           s = C('b');
Construct 'b'  
Assign 'b' onto 'a'  
Destroy 'b'         (Temporary destroyed at ';')  

>>          s = C('c');
Construct 'c'  
Assign 'c' onto 'b' (was 'a' but has already been assigned over)  
Destroy 'c'         (Temporary destroyed at ';')

>>          s = C('d');  
Construct 'd'  
Assign 'd' onto 'c'  
Destroy 'd'         (Temporary destroyed at ';')  

>> End of scope.
Destroy 'd'         (Object s destroyed at '}')  

コンパイラによって 4 つのメソッドが定義されているため、「4 つのルール」が適用されます。
クラスが所有する RAW ポインターがクラスに含まれている場合 (所有されているということは、オブジェクトが寿命を決定することを意味します)。次に、コンパイラが生成した 4 つのメソッドすべてをオーバーライドする必要があります。

メンバー「ptr」を作成して破棄するため、これは所有されているptrです。したがって、4 つのメソッドすべてを定義する必要があります。

于 2009-10-07T12:42:02.227 に答える
1

生のポインターを所有するすべての型に対して行う必要があるように、コピーコンストラクターと代入演算子を作成できます。

于 2009-10-07T12:24:38.200 に答える
1

sはスコープ外に出たときにのみ破棄されます。また、おっしゃるように、プログラムの過程で上書きされるため、最初の割り当てがリークされ、最後の割り当てが二重に削除されます。

解決策は、代入演算子をオーバーロードすることです(そして、Peteが提案するように、それらが連携するときにコピーコンストラクターを提供します)。ここで、所有している配列をクリーンアップし、指定された配列をコピーします。

于 2009-10-07T12:25:59.117 に答える
1

問題は、コピー コンストラクターと代入演算子が必要なことです。あるクラスを別のクラスに割り当てる行により、浅いコピーが作成されます。これにより、両方のクラスが同じ ptr ポインターを持つことになります。それらの1つが削除された場合、もう1つはすでに解放されたメモリのトップを指します

于 2009-10-07T12:28:57.723 に答える
0

You haven't defined an assignment or copy operator. So whats happening is something like:

C s('a');

The 's' instance gets created and initialized with 'a'.

s = C('b');

This creates a temporary object, initializes it with 'b', and then the default assignment operator kicks in which does a bitwise copy of all variables, overwriting the ptr of s. The temporary object is destroyed. Emitting 'b' and deleting 'ptr' (rendering ptr in s invalid).

s = C('c');
s = C('d');

Same again. Temporary object is created, initialized with 'c', and the 'ptr' in s is overwritten with the ptr allocated in the temporary object. The temporary object is destroyed, emitting 'c' and leaving the ptr in s invalid. Repeat for d.

  return 0;
}

Finally s leaves scope, its destructor tries to emit the first character of ptr, but thats garbage because ptr was allocated and deleted by the last ('d') temporary object. The attempt to delete ptr should fail as that memory is already deleted.

To solve this? Define explicit copy constructors and assignment operators.

class C {
  // other stuff
  C(const C&rhs); // copy constructor
  C& operator=(const c& rhs){ // assignment operator
    a[0] = rhs.a[0];
    return *this;
  }
};
于 2009-10-07T12:37:25.573 に答える