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

标签 c free delete-operator

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

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

最佳答案

delete在C++中有两种概念:一种是operator,声明为::operator delete(void*),它基本上只释放内存,大多数程序员通常不会考虑。另一个是delete 表达式delete p;,其中p 是一个T*。该表达式调用 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 expressions 实现的:标准的 new 表达式调用构造函数(new 表达式是在 C++ 中调用构造函数的唯一方法!)在分配之后,而标准 delete 表达式在释放之前调用析构函数。

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

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; }

匹配delete operator也是强制的,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);

关于c - C 中的 free 和 C++ 中的 delete 之间的区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6816791/

相关文章:

c++ - 删除指向 C++ 列表的指针非常非常慢。为什么?

C++ 数组删除运算符语法

将秒(unix 时间)转换为人类可读的(不同的变量)

c++ - 通过 Linux 套接字接收多台主机的数据

c - 字典排序,C 语言编程,Stephen Kochan,第 10 章,练习 10

c - 从 C 中的动态数组中释放单个元素

c - 使用 "char"的 boolean 数组

c - C 中带有 %s 参数的 printf() 和 fprintf() 调用的奇怪行为

c - 免费通话时进行核心转储

c++ - 静态工厂方法和静态对象内存泄漏