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];
}
}
我目前拥有最上层的构造函数,一切正常,但当然我看不出它是否恰好泄漏......
就个人而言,我认为考虑到我正在做的事情,第二个版本更合乎逻辑。无论如何,这样做的“正确”方法是什么?
答案 0 :(得分:42)
delete[] monsters;
不正确,因为monsters
不是指向动态分配数组的指针,是指针数组。作为类成员,它将在类实例被销毁时自动销毁。
您的其他实现是正确的,因为数组中的指针确实指向动态分配的Monster
个对象。
请注意,使用当前的内存分配策略,您可能希望声明自己的复制构造函数和复制赋值运算符,以便无意复制不会导致双重删除。 (如果你想阻止复制,你可以将它们声明为私有而不是实际实现它们。)
答案 1 :(得分:37)
对于new
,您应该使用delete
。对于new[]
使用delete[]
。你的第二个变种是正确的。
答案 2 :(得分:11)
第二个在这种情况下是正确的(好吧,最不错的,无论如何)。
编辑:“最不合适”,因为在原始代码中显示没有充分理由首先使用new
或delete
,所以您应该只使用:
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)
您可以单独删除每个指针,然后删除整个数组。确保已为存储在数组中的类定义了正确的析构函数,否则您无法确定是否正确清理了对象。确保所有析构函数都是虚拟的,以便在与继承一起使用时它们的行为正常。