C中的free和C++中的删除之间的区别?

4 c free delete-operator

我知道C中的自由操作是告诉编译器这个特定的内存块是免费的,编译器可以用它来进一步分配,但内存没有被释放.

在C++中删除怎么样?和免费一样吗?

Ker*_* SB 20

delete在C++中有两种概念:一种是声明的运算符,::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
Run Code Online (Sandbox Code Playgroud)

高级部分(不是对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
Run Code Online (Sandbox Code Playgroud)

事实上,如果您真的想要实现Baby的First C++,您可以将运算符定义为malloc/ free:

void * operator new(size_t n) { return malloc(n); }
void   operator delete(void * p) { free(p); }
Run Code Online (Sandbox Code Playgroud)

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

为什么"标准表达"?好了,你也可以定义和超载等诸多版本newdelete运营商.但是,存在一个重要的不对称性:虽然您可以new在自定义new表达式中使用自定义运算符(通常称为"placement 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
Run Code Online (Sandbox Code Playgroud)

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

为了完整起见,标准放置新运算符(其唯一目的是调用构造函数)由标准强制执行以下形式:

void * operator new(size_t, void * p) { return p; }
Run Code Online (Sandbox Code Playgroud)

它的匹配delete 运算符也是强制性的,名称无所作为:

void operator delete(void * p, void *) { }
Run Code Online (Sandbox Code Playgroud)

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

始终重载自定义版本newdelete成对重要!原因是如果对象构造在构造函数内部因异常而失败,则通过调用delete与违规new表达式匹配的运算符来释放内存.


第二次更新:为了异常安全,我们必须考虑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(...) { }
Run Code Online (Sandbox Code Playgroud)

版本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);
Run Code Online (Sandbox Code Playgroud)

  • "原始"调用序列可以像这样手动写出:`T*p =(T*):: operator new(sizeof(T)); p = new(p)T; P->〜T(); :: operator delete(p);`你写的是疯了:-) (3认同)