2

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

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是否分配有newor new[],所以该函数如何正确地deleteor delete[]

4

6 回答 6

4

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

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

然后你必须释放对象。或者数组。

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

本质上,如果“函数不知道它的参数是否p被分配”,则该函数没有尝试解除分配的业务。如果参数在堆栈上怎么办?如果使用池分配器会怎样?newnew[]

此外,一旦将处理移至单独的函数,就可以轻松地为“处理然后删除单个对象”和“处理然后删除数组”创建一个函数,而不会重复(两者都调用处理组件的辅助函数)。

于 2012-04-21T15:49:24.647 回答
3

您不能(可移植地)检测到这一点并在函数中做正确的事情。

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

于 2012-04-21T15:42:46.103 回答
2

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

解决方案 2.如果您的编译器支持它们,请使用std::shared_ptrlambda 函数,并根据需要为每个指针使用不同的释放器。在此处阅读有关共享指针的信息。

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释放内存。这就是为什么当我创建第二个时,我只给它指向对象的指针,而不指定任何自定义释放器。

于 2012-04-21T15:56:42.817 回答
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::vectoror boost::scoped_ptrboost::scoped_array等,它们保证调用正确版本的delete运算符,正如它们的名称所示。

于 2012-04-21T15:54:00.187 回答
0

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

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

于 2012-04-21T15:42:26.883 回答
-2

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

不过一般来说,当您使用 new[] 时,运行时会额外分配 4 个字节。然后它将数组的大小存储在这些字节中,并返回分配 + 4 个字节。释放时,它将从您传递的指针中删除 4,读取大小并使用它来决定要调用多少个析构函数等。

您可以在C++ 常见问题解答中阅读更多相关信息

于 2012-04-21T15:43:08.787 回答