42 / 80 · C++11 · 约 11 分钟
new/delete 表达式与分配函数
new 表达式通常包含取得存储和初始化对象,delete 表达式通常包含析构和释放存储;operator new/operator delete 是其中的底层分配函数,不等同于完整表达式。理解这层区别才能正确处理异常、数组和原位构造。
表达式负责完整对象操作
new T(args) 是语言表达式,它选择合适的分配函数取得存储,并执行初始化。直接调用 ::operator new(sizeof(T)) 得到的是原始存储,不等于构造完成的任意 T。对应地,delete 表达式负责对象销毁流程,直接调用 operator delete 不会替你调用类析构函数。
new 不是总要请求新堆块。标准的非分配 placement new 接受调用者提供的地址,在已有存储中构造对象。调用者必须先保证尺寸、对齐和生存期条件,并自行安排最终归还存储;不能把该地址随便交给普通 delete。
数组、初始化与失败协议
new T 与 new T[n] 必须分别匹配 delete 与 delete[],数组释放需要处理所有元素。new int 没有初始化整数值,而 new int{} 将其值初始化为零。不要通过读取未初始化整数来演示区别,说明规则就足够。
通常的抛异常分配失败以 bad_alloc 报告,nothrow 分配形式失败返回空指针;但构造函数自己的异常仍可以从 nothrow new 表达式中传播。如果普通 new 表达式已经分配成功、随后构造抛异常,会调用匹配的释放函数归还这份存储。
展开步骤只用于底层封装
示例直接取得存储,立即放进使用 operator delete 的拥有者;然后 placement new 构造 Token,验证状态并显式析构。Token 构造与析构都不抛异常,示例访问区也没有可抛业务操作,所以清理顺序完整而可追踪。
真实业务若在构造后执行可抛操作,还需单独保证对象析构,不能只给原始存储加释放守卫。通常直接 make_unique 就已正确组合这些步骤。malloc 同样不负责构造,但它具有不同的失败和释放协议;不能因某实现内部复用 malloc 就把接口混用。
容易答错的地方
- 对 placement new 使用的自动数组或外部分配器存储执行普通 delete,会违反其释放协议。
- nothrow 只改变相关分配失败的报告方式,不是包住整个构造过程的异常吞掉开关。
运行一个例子
最低标准 C++11 · 完整程序 · 下载 .cpp
#include <cassert>
#include <memory>
#include <new>
struct Deallocate {
void operator()(void* p) const noexcept { ::operator delete(p); }
};
struct Token {
static int alive;
int value;
explicit Token(int n) noexcept : value(n) { ++alive; }
~Token() noexcept { --alive; }
};
int Token::alive = 0;
int main() {
std::unique_ptr<void, Deallocate> storage(::operator new(sizeof(Token)));
assert(Token::alive == 0);
Token* token = ::new (storage.get()) Token(7);
assert(Token::alive == 1 && token->value == 7);
token->~Token();
assert(Token::alive == 0);
}
在本地编译
g++ -std=c++11 -Wall -Wextra -Wpedantic -pthread memory-new-delete.cpp -o example && ./example预期结果
预期:正常退出、无输出;所有 assert 通过。
CHECK YOUR UNDERSTANDING
合上答案,试着解释。
直接调用 operator new 后再 placement new 构造 T,若 T 构造抛异常,谁归还之前申请的存储?
查看参考答案
标准非分配 placement new 对应的 placement delete 不会归还这块外部存储,责任仍属于调用者。可以像示例那样先建立原始存储拥有者,使构造失败时自动调用匹配的 operator delete。若改成普通 new T(args),表达式会在构造失败时处理与其分配相匹配的释放;更高层可直接使用 make_unique。
继续查证
标准草案链接会随工作草案更新;本文版本标记对应示例最低要求,不表示草案中的所有新规则都适用于旧标准。