C 中的 free 和 C++ 中的 delete 之间的区别?

Posted

技术标签:

【中文标题】C 中的 free 和 C++ 中的 delete 之间的区别?【英文标题】:The differences between free in C and delete in C++? 【发布时间】:2011-07-25 13:23:34 【问题描述】:

我知道 C 中的 free 操作是告诉编译器这个特定的内存块是空闲的,供编译器用于进一步分配,但内存没有释放。

C++ 中的删除呢?和免费一样吗?

【问题讨论】:

是的,是一样的。但是您可以重载新/删除实现并做自己的事情***.com/q/4261963/176769 @karlphilip delete 会调用析构函数,free 不会那样做,这是一个相当大的区别 你不告诉编译器,你告诉运行时。并且内存可能会或可能不会实际返回给操作系统,具体取决于特定于实现的条件。通常小块从不断增长的堆中获得(因此被重用但不返回),而大块作为单独的实体(使用 mmap 或类似实体)从系统中获得,并在释放时返回给系统。 【参考方案1】:

C++ 中有两个delete 的概念:一个是运算符,声明为::operator delete(void*),它基本上只是释放内存,大多数程序员通常不会考虑。另一个是删除表达式delete p;,其中pT*。该表达式调用 p 指向的对象的析构函数(然后释放内存),这是 C++ 的一个关键语言特性,在 C 中没有类似物。

根据经验,您可以将new 表达式与delete 表达式配对,并将malloc() 函数调用与free() 函数调用配对:

T * p = new T;        // constructor called!
delete p;             // destructor called!

void * x = malloc(5); // just raw memory
free(x);              // freed

高级部分(不是针对 OP 的问题)

C++ 中的动态对象生命周期遵循以下一般模式:分配、构造、销毁、解除分配。标准new 表达式执行分配和构造,而标准delete 表达式执行销毁和释放。

你可以手动写出这个过程:

T * p = (T*)::operator new(sizeof(T));   // allocate raw memory
p = new (p) T;                           // call the constructor ("placement new")

/*...*/

p->~T();                                 // destroy the object
::operator delete(p);                    // deallocate the memory

其实,如果你真的想实现Baby's First C++,你可以将操作符定义为malloc/free

void * operator new(size_t n)  return malloc(n); 
void   operator delete(void * p)  free(p); 

真正的 C++ 魔法通过 newdelete 表达式发生:标准的 new 表达式调用构造函数(new 表达式是 only 在分配之后调用 C++ 中的构造函数的方法!),而标准的删除表达式在释放之前调用析构函数。

为什么是“标准表达”?好吧,您还可以定义和重载许多其他版本的 newdelete 运算符。但是,有一个重要的不对称性:虽然您可以在自定义 new 表达式(通常称为“placement new”)中使用自定义 new 运算符,但没有等效的“placement-delete”表达式。所以每当你使用自定义的new 表达式时,你必须在调用匹配的自定义删除操作符之前手动调用析构函数:

T * p = new (A, B, C) T;                          // some custom new expression

// Entirely equivalent version:

T * p = (T*) ::operator new(sizeof(T), A, B, C);  // this is your custom overload
T * p = new (p) T;                                // std. placement-new expression calls constructor

/* ---- later ---- */

p->~T();                                          // Must destroy manually!
::operator delete(p, A, B, C);                    // your matching custom overload

请注意,不存在自定义删除表达式delete (A,B,C) p'

为了完整起见,标准放置 new 运算符的唯一目的是调用构造函数,标准要求采用以下形式:

void * operator new(size_t, void * p)  return p; 

匹配deleteoperator也是强制的,name什么都不做:

void operator delete(void * p, void *)  

您可以在上面的一般示例中看到为什么这是必要的。

务必成对重载newdelete 的自定义版本!原因是,如果对象构造失败并在构造函数中出现异常,则通过调用匹配有问题的new 表达式的delete 运算符来释放内存。


第二次更新:为了异常安全,我们必须考虑T的构造函数可能会抛出:

版本 1:

try 
  T * p = new (A, B, C) T;
  /* ... */
  p->~T();
  ::operator delete(p, A, B, C); // automatically invoked if T::T() throws!

catch(...)  

版本 2:

void * addr = ::operator new(sizeof(T), A, B, C);
try 
  T * p = new (addr) T;  // might throw
  /* ... */
  p->~T();
  // ::operator delete(p, addr); // ditto as in (1), but does nothing

catch(...)  
::operator delete(addr, A, B, C);

【讨论】:

更准确地说,delete 不调用析构函数;但是编译器在找到delete 运算符的任何地方之前都会调用析构函数。当你进入delete的函数块时,析构函数已经被调用了。 @iammilind:你的措辞有点不准确:如果你说::operator delete(p);,析构函数将不会被调用。析构函数调用是 delete 表达式的结果。 “原始”调用序列可以像这样手动写出:T * p = (T*)::operator new(sizeof(T)); p = new (p) T; p->~T(); ::operator delete(p); 你写那个太疯狂了:-) @Kerrek:为什么不将 cmets 添加到问题文本中?当您扩展自己的答案时,这是合适的。至于显式序列,当您为它们定义一些数据结构或分配器(如在 STL 容器的自定义分配器中)时,这一点很重要。 std::vector 之类的东西必须分配内存块,但稍后再初始化它。这不是一个人应该经常做的事情,但是当你追求表现时,你最终会需要它。 @Jan:我知道您确实在后台使用了显式分配和构造。我只是认为这超出了 OP 的兴趣范围。这也是为什么我不想在这个问题中添加operator new 替换和分配器的细节。不过,如果你愿意,我可以做到。【参考方案2】:

deletefree 相同(ish),但有重要区别。最显着的区别是delete 将运行对象析构函数,而free 不会。

正如 cmets 所指出的,另一个非常重要的细节是不要混合 malloc/free 和 new/delete。如果您使用 malloc 分配,请使用 free,如果您使用 new,请使用 delete!

【讨论】:

使用new 分配并尝试使用free() 释放或使用malloc() 分配并尝试使用delete 释放是未定义的行为。如果您尝试这样做,某些实现会崩溃! 这样可以吗,new=>(malloc)=>constructor; delete=>destructor=>(free) ?其中 (malloc,free) 被称为“内部/自动”。我说得对吗?【参考方案3】:

它们不一样。 C++ 中的delete 运算符调用对象的析构函数,根据实现,它应该释放内存。 delete 函数也可以重载,而free 不能。

【讨论】:

不是函数,是运算符。 @Jan Hudec:非常正确。已编辑。谢谢。 @ratzip:只需调用delete 将调用该对象的默认析构函数,释放内存。 @Evan:形式上是错误的。它将调用析构函数释放内存。它们是两个独立的步骤,后者可以通过定义适当的operator delete() 方法(以及相应的operator new())来覆盖每个类型。【参考方案4】:

malloc() 和 free() 不能在 C++ 代码中使用,因为它们不支持对象语义。此外,调用 free() 释放由 new 分配的对象,或使用 delete 释放由 malloc() 分配的内存的结果是未定义的。 C++ 标准不保证 operator new 的底层实现使用 malloc();事实上,在某些实现中 malloc() 和 new 使用不同的堆。 检查这个link

delete() 将删除占用的整个内存空间,一旦它被删除,在 free() 中你仍然可以访问它,就无法引用它。

【讨论】:

以上是关于C 中的 free 和 C++ 中的 delete 之间的区别?的主要内容,如果未能解决你的问题,请参考以下文章

c的free和c++的delete的区别

Luweir大厂面试准备:每日五题(C++)

c++面试题1

C++内存管理第一篇:(malloc/deldete和malloc/free)

C语言中已经有了malloc和free,为啥还需要new和delete?

C++面试题