57

次のコードを検討してください。

class Foo
{
    Monster* monsters[6];

    Foo()
    {
        for (int i = 0; i < 6; i++)
        {
            monsters[i] = new Monster();
        }
    }

    virtual ~Foo();
}

正しいデストラクタは何ですか?

これ:

Foo::~Foo()
{
    delete [] monsters;
}

またはこれ:

Foo::~Foo()
{
    for (int i = 0; i < 6; i++)
    {
        delete monsters[i];
    }
}

私は現在、最上位のコンストラクターを持っており、すべて正常に動作していますが、もちろん、リークが発生しているかどうかはわかりません...

個人的には、私がやっていることを考えると、2 番目のバージョンの方がはるかに論理的だと思います。とにかく、これを行う「適切な」方法は何ですか?

4

8 に答える 8

64

delete[] monsters;

monstersは動的に割り当てられた配列へのポインターではなく、ポインターの配列であるため、正しくありません。クラス メンバとして、クラス インスタンスが破棄されると自動的に破棄されます。

配列内のポインターが動的に割り当てられたMonsterオブジェクトを指しているため、他の実装は正しい実装です。

現在のメモリ割り当て戦略では、意図しないコピーによって二重削除が発生しないように、独自のコピー コンストラクターとコピー代入演算子を宣言する必要があることに注意してください。(コピーを防ぎたい場合は、それらを非公開として宣言し、実際には実装しないでください。)

于 2010-05-11T20:30:50.420 に答える
50

を使用するnew必要がありますdeletenew[]使用するためdelete[]。2番目のバリアントは正しいです。

于 2010-05-11T20:31:18.917 に答える
14

回答を簡素化するために、次のコードを見てみましょう。

#include "stdafx.h"
#include <iostream>
using namespace std;

class A
{
private:
    int m_id;
    static int count;
public:
    A() {count++; m_id = count;}
    A(int id) { m_id = id; }
    ~A() {cout<< "Destructor A "   <<m_id<<endl; }
};

int A::count = 0;

void f1()
{   
    A* arr = new A[10];
    //delete operate only one constructor, and crash!
    delete arr;
    //delete[] arr;
}

int main()
{
    f1();
    system("PAUSE");
    return 0;
}

出力は次のとおりです: Destructor A 1 そして、クラッシュしています (式: _BLOCK_TYPE_IS_VALID(phead-nBlockUse))。

以下を使用する必要があります。1つのセルだけでなく、配列全体を削除するためです!

delete[] arr; を使用してみてください。出力は次のとおりです。 デストラクタ A 10 デストラクタ A 9 デストラクタ A 8 デストラクタ A 7 デストラクタ A 6 デストラクタ A 5 デストラクタ A 4 デストラクタ A 3 デストラクタ A 2 デストラクタ A 1

同じ原則は、ポインターの配列にも当てはまります。

void f2()
{
    A** arr = new A*[10];
    for(int i = 0; i < 10; i++)
    {
        arr[i] = new A(i);
    }
    for(int i = 0; i < 10; i++)
    {
        delete arr[i];//delete the A object allocations.
    }

    delete[] arr;//delete the array of pointers
}

delete[] arr の代わりに delete arr を使用する場合。配列内のポインター全体を削除しません => ポインター オブジェクトのメモリ リーク!

于 2012-11-20T16:05:56.073 に答える
13

2 番目のものは、この状況下では正しいものです (とにかく、最も間違っていません)。

new編集:元のコードでは、最初にまたはを使用する正当な理由がないため、「最も間違っていない」ため、deleteおそらく次を使用する必要があります。

std::vector<Monster> monsters;

その結果、コードが単純になり、責任が明確に分離されます。

于 2010-05-11T20:31:22.057 に答える
8

delete[] monstersは間違いなく間違っています。私のヒープ デバッガーは、次の出力を示しています。

allocated non-array memory at 0x3e38f0 (20 bytes)
allocated non-array memory at 0x3e3920 (20 bytes)
allocated non-array memory at 0x3e3950 (20 bytes)
allocated non-array memory at 0x3e3980 (20 bytes)
allocated non-array memory at 0x3e39b0 (20 bytes)
allocated non-array memory at 0x3e39e0 (20 bytes)
releasing     array memory at 0x22ff38

ご覧のとおり、間違った形式の削除 (非配列と配列) で解放しようとしており、ポインター 0x22ff38 が new の呼び出しによって返されたことはありません。2 番目のバージョンは、正しい出力を示しています。

[allocations omitted for brevity]
releasing non-array memory at 0x3e38f0
releasing non-array memory at 0x3e3920
releasing non-array memory at 0x3e3950
releasing non-array memory at 0x3e3980
releasing non-array memory at 0x3e39b0
releasing non-array memory at 0x3e39e0

とにかく、そもそもデストラクタを手動で実装する必要がない設計を好みます。

#include <array>
#include <memory>

class Foo
{
    std::array<std::shared_ptr<Monster>, 6> monsters;

    Foo()
    {
        for (int i = 0; i < 6; ++i)
        {
            monsters[i].reset(new Monster());
        }
    }

    virtual ~Foo()
    {
        // nothing to do manually
    }
};
于 2010-05-11T21:01:51.463 に答える
3

2 番目の例は正しいです。配列自体を削除する必要はなくmonsters、作成した個々のオブジェクトだけを削除する必要があります。

于 2010-05-11T20:30:54.193 に答える
1

あなたのコードがこのようであるならば、それは意味をなすでしょう:

#include <iostream>

using namespace std;

class Monster
{
public:
        Monster() { cout << "Monster!" << endl; }
        virtual ~Monster() { cout << "Monster Died" << endl; }
};

int main(int argc, const char* argv[])
{
        Monster *mon = new Monster[6];

        delete [] mon;

        return 0;
}
于 2010-05-11T21:10:34.067 に答える
0

各ポインターを個別に削除してから、配列全体を削除します。配列に格納されているクラスに対して適切なデストラクタを定義したことを確認してください。そうしないと、オブジェクトが適切にクリーンアップされていることを確認できません。継承で使用したときに適切に動作するように、すべてのデストラクタが仮想であることを確認してください。

于 2010-05-11T20:32:10.463 に答える