C++中的delete与NULL与free有什么区别?

48

删除指针、将其设置为null和释放指针之间有什么区别。

delete ptr;

对比。

ptr=NULL;

vs.

:对比。
free(ptr);

2
C语言没有delete概念,你是指只有C++才有吗? - GManNickG
3
您的问题表明您来自一种像Java或C#这样具有"垃圾回收"功能的语言背景。而C++并没有这个功能。请查看我的回答以了解更多细节。 - j_random_hacker
@GMan,C语言中有自由(free)思想,从这个角度来看本质上是相同的。 - Steve Rowe
1
@Steve:嗯,“free”只是返回使用“malloc”分配的内存。“delete”调用析构函数、“operator delete”等。 - GManNickG
同意Potatoswatter的观点。至少我认为,如果在delete ptr;ptr=NULL;之间再加一个“vs.”,这个问题会更有意义。 - j_random_hacker
@GMan,你是正确的,但我认为不是相关的方式。这是关于将指针设置为NULL和调用delete之间的区别。最大的区别是内存泄漏。同样适用于free与将指针设置为NULL。 - Steve Rowe
6个回答

65

根据您的问题,您来自一种具有垃圾收集功能的语言。而C++没有垃圾收集

如果您将指针设置为NULL,这并不会导致内存返回到可用内存池中。如果没有其他指针指向此内存块,则现在您只有一个“孤立”的内存块仍然被分配,但是现在无法访问 -- 一个泄漏(leak)。只有当内存耗尽时,泄漏才会导致程序崩溃。

还有另一种相反的情况,即您使用指针delete一个内存块,然后稍后尝试访问该内存,就好像它仍然被分配一样。这是可能的,因为在指针上调用delete并不会将指针设置为NULL--它仍然指向以前分配的内存地址。指向不再分配的内存的指针称为悬垂指针(dangling pointer),访问它通常会导致奇怪的程序行为和崩溃,因为其内容可能不是您期望的内容 -- 这段内存可能已经为其他目的重新分配了。

[编辑]如stinky472所提到的,deletefree()之间的另一个区别是只有前者调用了对象的析构函数。 (请记住,您必须对使用new分配的对象调用delete,并对使用malloc()分配的内存调用free() -- 它们不能混合使用。) 在C++中,如果可能的话最好使用静态分配,但如果不行,则更喜欢使用new而不是malloc()


2
此回答是针对问题的第一个版本:“删除指针和将其设置为NULL之间有什么区别?” - j_random_hacker
1
在这个新版本中还值得指出的是,free不会调用UDT的析构函数,在C++中应该尽量避免使用它(除非正在实现内存分配器)。 - stinky472
此外,在C++中对非POD类型使用malloc/free/realloc/calloc等函数会产生未定义的行为。 - Sebastian Mach
@phresnel:我认为那里唯一有问题的是 realloc(),它允许移动内存中的项目。在其他情况下,您可以使用放置new和显式析构函数调用来管理对象生命周期而不会产生UB,尽管这当然有点不方便和容易出错。 - j_random_hacker
我今天想到了一个问题。当函数结束时,它实际上会释放指针(将它们释放),但不会删除它们,对吗?因为删除指针意味着同时删除其引用的对象。当编写一个向链表添加节点的函数时,我们很可能会在其中使用一个临时指针(称之为tmp),但当函数结束时,我们不能通过访问tmp来取消引用该节点,但是之前由tmp引用的节点仍然存在,并已被添加到列表中,对吗?tmp指针在函数之后被释放,但没有被删除,对吗? - Hang
显示剩余4条评论

31
delete将释放已分配的内存给C++运行库。在此之前需要使用匹配的new在堆上分配内存。NULL是指向不存在地址的“占位符”。在C++中,NULL是一个宏定义为0。因此,如果不喜欢宏,也可以直接使用0。在C++0x中引入了nullptr,并被推荐使用。

示例:

int* a;        //declare pointer
a = NULL;      //point 'a' to NULL to show that pointer is not yet initialized 
a = new int;   //reserve memory on the heap for int

//... do more stuff with 'a' and the memory it points to

delete a;      //release memory
a = NULL;      //(depending on your program), you now want to set the pointer back to
               // 'NULL' to show, that a is not pointing to anything anymore

3
技术上讲,你把它交还给C运行时库,然后该库可能会或可能不会将其返回给操作系统。 - shoosh
6
实际上,你将其返回给 C++ 运行时库,该库可能会将其返回给 C 运行时库或操作系统。 - MSalters

5

如果您使用“new”动态创建对象,则将指针设置为任何值都不会从内存中删除对象,这将导致内存泄漏。


5
与任何间接引用一样,使用指针时涉及到两个对象:引用者(指针,在您的示例中为`ptr`)和被引用对象(指针指向的内容,即`*ptr`)。您需要学会区分它们。
当您将`NULL`分配给指针时,只有指针受到影响,被引用的对象不受影响。如果该指针是最后一个指向该对象的指针,则已经失去了指向该对象的最后一个指针,因此无法再使用它。然而,在C++中,这并不意味着对象将被删除。C++没有垃圾回收。所以这个对象泄漏了。
为了删除对象,您必须通过将其地址(存储在指针中)传递给`delete`运算符来手动删除它们。如果您这样做,只有被引用的对象会受到影响,指针保持不变。它可能仍然指向对象在内存中所在的地址,尽管那里已经无法使用。这就是所谓的悬挂指针。

1
@hero:你使用new获取的对象应该使用delete进行删除,使用new[]获取的数组应该使用delete[]进行删除,使用malloc()获取的内存应该使用free()进行释放。然而,在C++中,你不应该使用malloc()。它所做的是分配动态__内存__,而new则创建动态分配的__对象__。原则上,new所做的是通过调用类型的构造函数来分配内存和创建对象 - sbi
@FredOverflow:在C++中,int也被称为“对象”。我认为你的意思是它不一定是一个左值,它也可以是一个右值。 - sbi
@sbi 我说的不是字面意义上的 42,它只是一个表达式,而是值为42,这是通过计算表达式 42 得出的结果。lvalues和rvalues都是一种表达式,不是对象的一种。int 类型的 lvalue 是产生 int 对象的表达式,而 int 类型的 rvalue 是产生 int 值的表达式。(对于类类型,lvalues 和 rvalues 都会生成对象。)如果你在标准中搜索“对象表示”和“值表示”,你会发现对象和值完全不同。 - fredoverflow
@FredOverflow,最近我对于对象是lvalues/rvalues还是只有表达式可以是这样的东西有些不确定了。即使没有表达式引用它们,临时对象似乎具有这种属性。但我相信这只是不幸的措辞。你认为呢?是否需要发表一条FCD评论? - Johannes Schaub - litb
@Johannes 当前标准§3.10 1明确规定“每个表达式都是左值或右值”,而当前草案将表达式明确分为左值、x值、pr值、gl值和右值(请参见第76页的图表)。 - fredoverflow
显示剩余6条评论

3

当您使用new创建一个对象时,您需要使用delete来释放其内存以返回给系统。这样,该内存将可供其他人重用。


int* a = new int;
delete a;

NULL是一个预定义的宏,可以分配一个指针来表示它不指向任何东西。


int* a = new int;
a = NULL;

在上述情况中,分配了a的存储空间后,您将其赋值为NULL。然而,先前为a分配的内存未被释放,系统无法重复使用该内存。这就是我们所说的内存泄漏。

1
int * ptr = null;

调用free(ptr)将不会有任何作用。根据标准,如果给定的指针为空,则不会发生任何操作。

使用delete p也将不会有任何作用。C++标准规定,因为它指向空值且没有可用类型,所以不会发生任何操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接