C++ 中有两个delete 的概念:一个是运算符,声明为::operator delete(void*),它基本上只是释放内存,大多数程序员通常不会考虑。另一个是删除表达式,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++ 魔法通过 new 和 delete 表达式发生:标准的 new 表达式调用构造函数(new 表达式是 only 在分配之后调用 C++ 中的构造函数的方法!),而标准的删除表达式在释放之前调用析构函数。
为什么是“标准表达”?好吧,您还可以定义和重载许多其他版本的 new 和 delete 运算符。但是,有一个重要的不对称性:虽然您可以在自定义 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 *) { }
您可以在上面的一般示例中看到为什么这是必要的。
务必成对重载new 和delete 的自定义版本!原因是,如果对象构造失败并在构造函数中出现异常,则通过调用匹配有问题的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);