删除动态分配的内存

Posted

技术标签:

【中文标题】删除动态分配的内存【英文标题】:Deleting dynamically allocated memory 【发布时间】:2013-12-27 16:02:14 【问题描述】:

我在动态内存分配方面遇到了一些概念问题。首先,如果我编写以下代码

int *p = NULL;
delete p;

为什么我没有错误?我正在尝试删除未指向任何内容的指针(在堆栈上)。另外,如果我写以下语句

int *p = new int;
p = NULL;
delete p;

我再次没有得到编译或运行时错误。为什么 ? 如果我编写以下代码继续前进,我会收到运行时错误

int *p = new int;
p = NULL;
delete p;
delete p;

为什么?如果我编写以下代码,则不会出错

int *p = NULL;
delete p;
delete p;

为什么?谁能从概念上解释这背后的原因?

【问题讨论】:

您可以安全地删除 NULL 指针。 你确定你的第三个例子吗?你不应该得到任何错误。如果您没有在第一个 delete 之前将 NULL 分配给 p,情况会有所不同 第三个例子,“我得到一个运行时错误”——不,你没有。该代码中没有运行时错误。存在 logic 错误,因为您正在泄漏内存,但是在 NULL 指针上调用 delete 运算符是无操作且完全合法的。 (这也解释了你的第一个和第四个例子没有任何错误)。 【参考方案1】:

我假设在你的第三个例子中你打算写

int *p = new int;
delete p;
delete p;

正式地说,这会导致未定义的行为,这意味着任何事情都可能发生。实际上,您可能正在使用内存分配器来检查您要删除的指针是否指向其空闲内存池中。

其他人已经指出删除空指针不会导致定义错误,因此执行多少次并不重要。

【讨论】:

【参考方案2】:

将空指针传递给删除运算符是无操作的。标准是这样说的:

5.3.5/2

在任一备选[delete和delete[]]中,如果delete的操作数的值是空指针,则操作无效。


考虑一个拥有指向另一个对象的指针的对象。通常,当拥有对象的析构函数运行时,它会通过删除来清理拥有对象的内存。但是在拥有的对象也可能为空的情况下,我们将如何清理内存?一种选择是将每个删除都包装在“if (X) delete x”类型的包装器中。但这太吵了,没有真正的额外好处。因此,delete 运算符会为您完成。

【讨论】:

【参考方案3】:

“我正在尝试删除没有指向任何东西的指针(在堆栈上)。”

这不是真的。您不能从堆栈中删除。使用delete,您可以删除地址存储在指针中的堆上的内存块。指针本身就是一个栈变量。

【讨论】:

【参考方案4】:

在每种情况下,您只是删除一个空指针,根据定义,它始终是“安全的”,因为它是一个无操作(C++ 标准明确表示)。

在第二个和第三个示例中,您在删除指针之前重新为其分配了一个新值(空指针),这意味着您正在泄漏先前分配的整数。这是通常不应该发生的事情(在这种情况下,您不会因泄漏单个整数而死,但这不是一件好事)。

第三个和第四个示例中的双重删除通常是严重的编程错误,但在您的示例中它们是“无害的”,因为删除的指针是空指针(因此它是空操作)。


有点 O/T: 请注意,我在上面的引号中加上“安全”和“无害”是有充分理由的。我个人不同意 Stroustrup 先生在这里的设计决定。 使删除空指针成为“无害的无操作”实际上不是一个好主意,即使意图可能是好的。 Stroustrup 先生甚至更进一步,允许delete 将指针设置为空指针,并说他希望实现确实做到了这一点(幸运的是,我所知道的没有实现!)。

在我看来,每个分配的对象都应该被删除一次,不能少也不能更频繁。

一个行为良好、未损坏的程序何时以及多久可以(并且必须)删除一个指针是完全定义的,它不是随机未知的事情。删除必须恰好发生一次,并且程序必须准确地知道它,因为它必须确定对象是否有效(因为如果对象无效,则使用该对象是非法的!)。

在删除对象后设置指向空指针的指针会在之后取消引用已删除对象时导致错误(这是一件好事),但它确实防止双重删除。相反,它隐藏这个严重的编程错误,默默地忽略它。

如果一个程序两次删除一个指针,那么程序逻辑就被破坏了,它不能正常工作。这不是可以忽略的事情,必须解决。因此,这样的程序应该崩溃。分配器通常会检测双重删除,但通过重置指向空指针的指针,可以有效地禁用这种检测机制。

如果选择在删除指针后重置它,应该(在我看来)将其设置为无效的非空指针值,例如(T*)1(T*)-1。这将保证 取消引用和删除指针都会在第一次崩溃。

没有人喜欢看到程序崩溃。但是,与不正确的程序逻辑持续不确定的时间并可能在随机情况下崩溃或静默破坏数据相比,提前崩溃和第一次崩溃是一件好事

【讨论】:

【参考方案5】:

我认为如果你试图删除指针,你实际上是在删除指针指向的对象在内存中的位置。您可以使用参考来做到这一点:

int *p = NULL;

delete &p;

【讨论】:

&p 是指向 int 的指针,而不是引用。你的例子是错误的。【参考方案6】:

内部实现对我们程序员来说是透明的。如您所见,deleteNULL 指针可能是无害的,但通常您应该避免这种情况。您可能已经看到诸如“请不要重新删除动态指针”之类的词

【讨论】:

不,你不应该避免它;你应该拥抱它。特别是在析构函数中。如果您的类在那里拥有一个指针,并且如果它指向实际对象,通常需要将其删除,那么您可以删除它。根本不需要检查 null 。这就是此功能的确切用例。 "但通常你应该避免这种情况" 为什么?

以上是关于删除动态分配的内存的主要内容,如果未能解决你的问题,请参考以下文章

我可以删除以前动态分配但使用不同指针的内存吗?

在 C++ 中的 2D 动态内存分配数组中释放分配的内存

对删除分配给结构数组的动态内存感到困惑

为动态数据结构预分配内存

动态内存分配

堆区的动态内存分配