C ++指针数组:删除或删除[]?

时间:2010-05-11 20:28:26

标签: c++ arrays pointers delete-operator

Cosider以下代码:

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

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

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

8 个答案:

答案 0 :(得分:42)

delete[] monsters;

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

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

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

答案 1 :(得分:37)

对于new,您应该使用delete。对于new[]使用delete[]。你的第二个变种是正确的。

答案 2 :(得分:11)

第二个在这种情况下是正确的(好吧,最不错的,无论如何)。

编辑:“最不合适”,因为在原始代码中显示没有充分理由首先使用newdelete,所以您应该只使用:

std::vector<Monster> monsters;

结果将是更简单的代码和更清晰的责任分离。

答案 3 :(得分:11)

为了简化answare,我们来看看以下代码:

#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;输出是: 析构函数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。它不会删除数组中的整个指针=&gt;指针对象的内存泄漏!

答案 4 :(得分:6)

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

正如您所看到的,您尝试使用错误的删除形式(非数组与数组)进行发布,并且调用new时从未返回指针0x22ff38。第二个版本显示正确的输出:

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

答案 5 :(得分:3)

你的第二个例子是正确的;您不需要删除monsters数组本身,只需要删除您创建的单个对象。

答案 6 :(得分: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;
}

答案 7 :(得分:0)

您可以单独删除每个指针,然后删除整个数组。确保已为存储在数组中的类定义了正确的析构函数,否则您无法确定是否正确清理了对象。确保所有析构函数都是虚拟的,以便在与继承一起使用时它们的行为正常。