在接收动态数组或堆对象时,我应该使用delete还是delete []?

时间:2012-04-21 15:37:48

标签: c++ pointers memory-management

说我在堆上有两个项目:

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

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

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

可以这样调用此函数:

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是否已分配newnew[],因此该函数如何正常deletedelete[]

6 个答案:

答案 0 :(得分:4)

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

您有一些操作在单个对象上的处理,因此传递单个对象或数组是合理的。 (为什么这个处理对于除第一个或非动态对象之外的数组元素也没有用?)

然后你必须释放这个对象。或阵列。

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

基本上,如果“函数不知道其参数p是否已分配newnew[]”,则该函数无法尝试解除分配。如果参数在堆栈上怎么办?如果使用池化分配器怎么办?

此外,一旦处理移动到单独的函数,就可以轻松地为“进程然后删除单个对象”以及“处理然后删除数组”而不复制(两者都调用处理组件的辅助函数) )。

答案 1 :(得分:3)

你不能(便携地)检测到并在函数中做正确的事情。

“解决方法”是使用std::vector<Foo>而不是数组,并始终使用delete

答案 2 :(得分:2)

解决方案1。从代码设计的角度来看,您应该完全避免这种情况,并让对象被同一个类,一对create-destroy函数或代码删除分配它的块。这样,你知道你没有犯任何错误。

解决方案2. 如果编译器支持它们,请使用std::shared_ptr和lambda函数,并根据需要为每个指针使用不同的deallocator。阅读有关共享指针here的信息。

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

第一个,f是指向数组的共享指针。当我创建它时,我作为第一个参数给出一个(正常)指向数组的指针;第二个参数是 lambda函数,它采用Foo* p参数,其正文为{ delete[] p; }

默认情况下,std::shared_ptr使用delete取消分配内存。这就是为什么当我创建第二个时,我只给它指向对象的指针,并且不指定任何自定义的deallocator。

答案 3 :(得分:1)

你做不到。你永远不知道指针是指向一个对象还是一个这样的对象的数组。

但是,如果你真的想要一个单独的函数来解除分配,那么如果你能记住分配像数组一样的东西,即使你正在分配一个对象,下面的用法是:合法的:

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运算符的正确版本,就像它们的名称所示

答案 4 :(得分:0)

你必须通过它。该函数无法在运行时确定。

请参阅here以获得更好的解释。

答案 5 :(得分:-2)

由编译器供应商决定如何实现这一点,因此属于“魔术”类别。

通常,运行时将在使用new []时额外增加4个字节。然后它将以这些字节存储数组的大小,并返回分配+4个字节。解除分配时,它将从你传递它的指针中删除4,读取大小并使用它来决定要调用多少个析构函数等。

您可以在C++ FAQ

中详细了解相关信息