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];
    }
}

我目前有最上面的构造函数,一切正常,但我当然看不到它是否恰好泄漏......

就我个人而言,考虑到我在做什么,我认为第二个版本更合乎逻辑。无论如何,这样做的“正确”方法是什么?

4

8 回答 8

64

delete[] monsters;

不正确,因为它不是monsters指向动态分配数组的指针,而是指针数组。作为类成员,它会在类实例被销毁时自动销毁。

您的其他实现是正确的,因为数组中的指针确实指向动态分配的Monster对象。

请注意,使用您当前的内存分配策略,您可能希望声明自己的复制构造函数和复制赋值运算符,以便无意复制不会导致双重删除。(如果你想防止复制,你可以将它们声明为私有而不实际实现它们。)

于 2010-05-11T20:30:50.420 回答
50

因为new你应该使用delete. 供new[]使用delete[]。您的第二个变体是正确的。

于 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;
}

输出是:析构函数 A 1,然后它崩溃了(表达式:_BLOCK_TYPE_IS_VALID(phead-nBlockUse))。

我们需要使用:delete[] arr; 因为它删除了整个数组,而不仅仅是一个单元格!

尝试使用 delete[] arr; 输出为: Destructor A 10 Destructor A 9 Destructor A 8 Destructor A 7 Destructor A 6 Destructor A 5 Destructor A 4 Destructor A 3 Destructor A 2 Destructor 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

在这种情况下,第二个是正确的(好吧,至少是错误的)。

编辑:“最少错误”,因为在原始代码中显示没有充分的理由使用newdelete首先,所以你可能应该只使用:

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 返回。第二个版本显示正确的输出:

[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

你的第二个例子是正确的;您不需要删除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 回答