4

次のコードフラグメントはリークしますか?そうでない場合、foobar()で構築された2つのオブジェクトはどこで破棄されますか?

class B
{
   int* mpI;

public:
   B() { mpI = new int; }
   ~B() { delete mpI; }
};

void foobar()
{
   B b;

   b = B();  // causes construction
   b = B();  // causes construction
}
4

5 に答える 5

7

デフォルトのコピー代入演算子は、メンバーごとのコピーを行います。

だからあなたの場合:

{
  B b;      // default construction.
  b = B();  // temporary is default-contructed, allocating again
            // copy-assignment copies b.mpI = temp.mpI
            // b's original pointer is lost, memory is leaked.
            // temporary is destroyed, calling dtor on temp, which also frees
            // b's pointer, since they both pointed to the same place.

  // b now has an invalid pointer.

  b = B();  // same process as above

  // at end of scope, b's dtor is called on a deleted pointer, chaos ensues.
}

詳細については、Effective C++、第 2 版の項目 11 を参照してください。

于 2010-12-03T00:53:53.257 に答える
4

はい、これは漏れます。追加のメソッドが定義されていないため、コンパイラは自動的に追加のメソッドを提供します。生成されるコードは次と同等です。

B & B::operator=(const B & other)
{
    mpI = other.mpI;
    return *this;
}

これは、次のことが起こることを意味します。

B b; // b.mpI = heap_object_1

B temp1; // temporary object, temp1.mpI = heap_object_2

b = temp1; // b.mpI = temp1.mpI = heap_object_2;  heap_object_1 is leaked;

~temp1(); // delete heap_object_2; b.mpI = temp1.mpI = invalid heap pointer!

B temp2; // temporary object, temp1.mpI = heap_object_3

b = temp1; // b.mpI = temp2.mpI = heap_object_3; 

~temp1(); // delete heap_object_3; b.mpI = temp2.mpI = invalid heap pointer!

~b(); // delete b.mpI; but b.mpI is invalid, UNDEFINED BEHAVIOR!

これは明らかに悪いことです。これは、3 つのルールに違反した場合に発生する可能性があります。重要なデストラクタとコピー コンストラクタを定義しました。ただし、コピーの割り当ては定義していません。3 つのルールは、上記のいずれかを定義する場合は、常に 3 つすべてを定義する必要があるということです。

代わりに、次の操作を行います。

class B
{
   int* mpI;

public:
   B() { mpI = new int; }
   B(const B & other){ mpI = new int; *mpi = *(other.mpI); }
   ~B() { delete mpI; }
   B & operator=(const B & other) { *mpI = *(other.mpI); return *this; }
};

void foobar()
{
   B b;

   b = B();  // causes construction
   b = B();  // causes construction
}
于 2010-12-03T01:04:13.970 に答える
3

3 つのオブジェクトを構築していて、すべてが破壊されます。問題は、デフォルトのコピー代入演算子が浅いコピーを行うことです。つまり、ポインタがコピーされ、複数回削除されます。これにより、未定義の動作が発生します。

これが3 のルールの背後にある理由です。デストラクタはありますが、他の 2 つはありません。コピー コンストラクターとコピー代入演算子を実装する必要があります。どちらもディープ コピーを実行する必要があります。これは、新しい int を割り当て、値をコピーすることを意味します。

B(const B& other) : mpI(new int(*other.mpI)) {
}

B& operator = (const B &other) {
    if (this != &other)
    {
        int *temp = new int(*other.mpI);
        delete mpI;
        mpI = temp;
    }
    return *this;
}
于 2010-12-03T00:53:25.133 に答える
0

最初に投稿したコードの問題を解決するための別のアプローチを提供するために、ポインタをクラス B に保持し、メモリ管理を取り除くことができると思います。次に、カスタム デストラクタは必要ないので、3 の規則に違反しません...

class B
{
   int* mpI;

public:
   B() {}
   B(int* p) { mpI = p; }
   ~B() {}
};

void foobar()
{
   int* pI = new int;
   int* pJ = new int;

   B b;        // causes construction

   b = B(pI);  // causes construction
   b = B(pJ);  // causes construction

   delete pI;
   delete pJ;
}
于 2010-12-03T01:21:10.053 に答える
0

何度か指摘したように、あなたは 3 つのルールに違反しています。リンクに追加するだけで、スタック オーバーフローに関する素晴らしい議論があります: What is The Rule of Three?

于 2010-12-03T01:14:43.737 に答える