重载全局 new 和 delete 必须成对实现,否则 new 失败时调用默认 delete 可能无法释放自定义内存;类内重载仅影响该类对象分配,需保证对齐;placement new 不可重载且无自动 delete;new[]/delete[] 也需成对重载。

重载全局 new 和 delete 时必须成对实现
只重载 operator new 而不重载 operator delete,会导致 new 失败或异常时调用默认 operator delete,而它可能无法正确释放你自定义分配的内存(比如你用了 mmap 分配页,但默认 delete 调用 free)。编译器不强制报错,但运行时容易崩溃或泄漏。
实操建议:
- 全局重载必须同时提供
void* operator new(std::size_t)和void operator delete(void*) - 如果还重载了带异常规格的版本(如
noexcept),也要补全对应delete,例如void* operator new(std::size_t, std::nothrow_t)必须配void operator delete(void*, std::nothrow_t) - 所有重载函数都应声明在全局作用域,且不能是类成员(那是类内重载)
类内重载 new/delete 只影响该类对象的动态分配
在类中声明 operator new 或 operator delete,仅控制 new MyClass 这类表达式的行为,不影响其成员、基类或栈上对象。它天然具备“静态绑定”特性——编译期就决定调用哪个版本,不走虚函数机制。
常见错误现象:
立即学习“C++免费学习笔记(深入)”;
- 子类没重载
operator delete,但父类重载了;当用delete ptr_to_base(ptr_to_base 实际指向子类对象)时,会调用父类的delete,而它可能不知道子类额外内存布局,导致未定义行为 - 类内重载的
operator new返回了对齐不足的地址(比如只按 4 字节对齐),而类含double成员(需 8 字节对齐),触发未定义行为
所以类内重载时,务必确保 operator new 返回满足 alignof(MyClass) 对齐要求的指针。
placement new 不可重载,但配套 delete 需手动管理
operator new(std::size_t, void*) 是 placement new,它是语言内置函数,**不能被重载**。你写同签名函数,编译器直接忽略,仍调用内置版本。它的作用只是返回传入的 void* 指针,不做任何分配。
关键点在于:placement new 不分配内存,因此没有对应的自动 operator delete 调用。如果你用它构造对象,必须显式调用析构函数,并自行管理原始内存的释放(比如用 free() 或 munmap())。
示例场景:
- 对象池中用
new (pool_ptr) MyClass(args)构造 → 后续必须ptr->~MyClass()+ 手动归还pool_ptr到池 - 误写
delete ptr(其中 ptr 来自 placement new)→ 触发operator delete(void*),试图释放非法地址,大概率 crash
重载数组形式 new[]/delete[] 容易遗漏且语义不同
重载 operator new[](std::size_t) 并不等于 “给数组分配内存”,它只是为 new T[N] 表达式分配足够容纳 N 个 T 的裸内存。C++ 标准不要求它存储数组长度,所以 delete[] 无法得知 N —— 这就是为什么你不能靠 delete[] 安全释放 placement new[] 构造的数组。
容易踩的坑:
- 只重载了
new和delete,但忘了new[]和delete[]→ 编译通过,但new int[10]会调用全局new[],而delete[]可能调用你重载的单对象delete(如果没提供delete[]),引发未定义行为 -
new[]返回的地址,不一定等于你实际 malloc 的起始地址(有些实现会在前面写长度),所以别假设偏移可算
若真需要定制数组分配,最稳妥方式是避免 new T[N],改用容器(如 std::vector)或显式分配+定位构造。


















