关于c ++:在接收动态数组或堆对象时,应该使用delete还是delete []?

Should I use delete or delete[] when receiving either a dynamic array or heap object?

假设我在堆上有两个项目:

1
2
Foo *f = new Foo;
Foo *g = new Foo[42];

并说我有一个接收Foo指针的函数,并且在该函数内它需要执行delete

1
2
3
4
void bar(Foo *p) {
    // some stuff
    delete p;
}

可以这样调用该函数:

1
2
bar(f); // passing a pointer to a Foo object on the heap
bar(g); // passing a pointer to an array on the heap

我认识到应该分别使用delete[]delete释放分配给new[]new的内存; 但是,由于该函数不知道其参数p是用new还是new[]分配的,该函数如何正确地deletedelete[]


听起来您对"单一责任原则"有疑问。

您需要对单个对象进行一定数量的处理,因此传递单个对象或数组是合理的。 (为什么该处理对除第一个对象或非动态对象之外的数组元素也没有用?)

然后,您必须释放该对象。或数组。

这里有三个不同的任务,需要三个不同的功能。

本质上,如果"该函数不知道其参数p是用new还是new[]分配的",则该函数没有试图取消分配的事务。如果参数在堆栈上怎么办?如果使用池分配器怎么办?

同样,一旦处理移到一个单独的函数,就很容易制作一个函数,"处理然后删除单个对象"以及"处理然后删除数组"而无需重复(都为处理组件调用辅助函数)。


您无法(方便地)检测到该错误并在函数中执行正确的操作。

一种"解决方法"是使用std::vector而不是数组,而始终使用delete


解决方案1.从代码设计的角度来看,您可能应该完全避免这种情况,并用同一类,一对创建破坏函数或分配该对象的代码块删除该对象。这样,您就知道自己没有犯任何错误。

解决方案2。如果编译器支持它们,请使用std::shared_ptr和lambda函数,并根据需要对每个指针使用不同的释放器。在此处了解共享指针。

1
2
std::shared_ptr<Foo> f(new Foo[20], [](Foo* p) { delete[] p; });
std::shared_ptr<Foo> g(new Foo);

第一个f是指向数组的共享指针。创建它时,我将指向数组的(正常)指针作为第一个参数。第二个参数是带Foo* p参数且其主体为{ delete[] p; }的lambda函数。

默认情况下,std::shared_ptr使用delete释放内存。这就是为什么当我创建第二个对象时,我只给它指向对象的指针,而没有指定任何自定义释放器的原因。


你不能您永远不会知道指针是指向一个对象还是该对象的数组。

但是,如果您确实要有一个用于释放的函数,并且即使您曾经分配一个对象,也曾经记得分配类似于数组的任何东西,则以下用法合法:

1
2
3
4
5
6
7
8
Foo *h = new Foo[0];
Foo *f = new Foo[1];
Foo *g = new Foo[42];

void bar(Foo *p) {
    // some stuff
    delete [] p;
}

但是,请记住,有时技术上可能并不意味着您应该使用它。这取决于您的用例,将对象视为数组的特例是否有意义。

更为优雅的C ++方法是使用std::vectorboost::scoped_ptrboost::scoped_array等,这保证调用正确版本的delete运算符,正如它们的名称所指示的那样。


您必须通过它是。该函数无法在运行时

请参阅此处以获得良好的解释。


由编译器供应商决定应如何实现,因此属于"魔术"类别。

通常,运行时将在使用new []时额外分配4个字节。然后它将在这些字节中存储数组的大小,并返回分配+ 4个字节。取消分配时,它将从传递给它的指针中删除4,读取大小并使用该值确定要调用的析构函数等。

您可以在C ++常见问题中阅读有关此内容的更多信息。