c++oding="utf-8" ?>
重写全局operator new和delete必须成对提供,含单对象、数组、对齐版本;均需noexcept,用malloc/free避免递归;禁止依赖未初始化全局对象,确保全生命周期安全。

重写全局operator new和operator delete必须成对提供
只重载operator new而不提供对应operator delete,会导致new失败时调用默认delete(可能崩溃或未定义行为),尤其在构造函数抛异常时必踩坑。C++标准要求:每个可抛异常的operator new都应有匹配的operator delete,否则内存泄漏或UB风险极高。
最简可行写法如下:
void* operator new(std::size_t size) noexcept {
void* ptr = std::malloc(size);
if (!ptr) throw std::bad_alloc{};
return ptr;
}
<p>void operator delete(void* ptr) noexcept {
std::free(ptr);
}</p>
-
noexcept是关键——与系统默认版本签名一致,否则链接失败 - 必须用
std::malloc/std::free而非new/delete,避免递归调用 - 不要在
operator new里直接throw std::bad_alloc;先尝试分配,失败再抛,否则绕过set_new_handler
处理数组版本operator new[]和operator delete[]不能省略
哪怕你从不写new int[10],STL容器(如std::vector)内部会调用它们。漏掉数组版本,轻则内存泄漏,重则释放时crash(因为delete[]可能传入错误地址或元数据)。
数组版必须严格对应:
void* operator new[](std::size_t size) noexcept {
return operator new(size); // 复用单对象版本即可
}
<p>void operator delete[](void* ptr) noexcept {
operator delete(ptr); // 必须调用单对象delete,不是free!
}</p>
- 数组
new[]返回的指针,delete[]必须交给operator delete[],不能交给operator delete - 但
operator delete[]内部可以复用operator delete逻辑——前提是你的operator delete能安全处理任意指针(包括空指针) - 别试图在
new[]里额外分配长度头信息:C++不保证delete[]会传回原地址,且标准库可能自己管理
带std::align_val_t的对齐版本必须显式声明(C++17起)
如果代码用了alignas(64)或new(align_val_t{64}),而你没提供对齐版本,链接器直接报错:undefined reference to operator new(unsigned long, std::align_val_t)。即使没显式用对齐,某些STL实现(如libc++)也会在内部调用它。
补全对齐版本只需加参数:
void* operator new(std::size_t size, std::align_val_t align) noexcept {
return std::aligned_alloc(static_cast<:size_t>(align), size);
}
<p>void operator delete(void* ptr, std::align_val_t) noexcept {
std::free(ptr);
}</p></:size_t>
-
std::aligned_alloc要求size是align的整数倍,实际使用中建议向上对齐(如用std::ceil或位运算) - 注意
operator delete带align_val_t参数时,必须也声明对应版本,否则delete时链接失败 - Windows下
_aligned_malloc/_aligned_free替代std::aligned_alloc(后者在MSVC 2019前不支持)
替换全局操作符后,静态/线程局部对象的构造顺序容易出问题
全局operator new在main()之前就生效,但此时std::malloc等基础设施可能未初始化(尤其在动态库中)。更危险的是:如果自定义new依赖某个全局对象(比如日志器、内存池),而该对象自身又用new分配——必然死锁或访问未初始化内存。
- 绝对不要在
operator new里调用任何可能触发new的代码(包括std::cout、std::string构造、STL容器) - 调试时可用
malloc+write(2)打印日志,绕过C++运行时依赖 - 若需线程安全,用
std::atomic_flag或pthread_mutex_t(确保其静态初始化不依赖new) - 测试务必覆盖:全局对象构造、static局部变量首次进入、atexit注册函数执行阶段
真正难的从来不是写几行重载函数,而是确保它在程序生命周期每个角落都安全——尤其是那些你根本没意识到正在调用它的时刻。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











