重载了 C++ 全局的 operator new/operator delete(包括普通版和数组版) ,并通过打印日志的方式,直观展示这些底层内存分配 / 释放函数的调用时机和参数,帮你看清 new/delete 关键字背后的实际执行逻辑。
整体功能总结
这段代码的核心目的是:
- 重载全局作用域的
operator new/operator new[](内存分配)和operator delete/operator delete[](内存释放); - 在重载函数中添加打印日志,追踪
new int、new int[10]等操作实际调用的底层函数,以及传递的参数(分配 / 释放的内存大小); - 严格遵循 C++ 标准对这些重载函数的约束(比如处理
sz=0、抛出bad_alloc、非内联等)。
逐部分拆解代码
一、头文件说明
#include <cstdio> // 提供 printf/puts 等打印函数
#include <cstdlib> // 提供 malloc/free 等 C 语言内存函数
#include <new> // 提供 std::bad_alloc 异常类型
- 重载
operator new需要依赖<new>头文件中的std::bad_alloc(内存分配失败时抛出的异常); - 底层用 C 语言的
malloc/free实现内存分配 / 释放(这也是标准库默认operator new的常见实现方式)。
二、重载普通版 operator new(对应 new 单个对象)
// no inline, required by [replacement.functions]/3
void* operator new(std::size_t sz)
{
std::printf("1) new(size_t), size = %zu\n", sz);
if (sz == 0)
++sz; // avoid std::malloc(0) which may return nullptr on success
if (void *ptr = std::malloc(sz))
return ptr;
throw std::bad_alloc{}; // required by [new.delete.single]/3
}
- 核心作用 :替换全局的 "单个对象内存分配函数",
new int、new MyClass等操作会调用这个函数; - 关键细节 :
no inline约束:C++ 标准要求重载的全局operator new不能是内联函数(否则无法正确替换默认版本);sz == 0处理:std::malloc(0)的行为是未定义的(可能返回nullptr),因此强制把sz=0改成1,保证malloc调用合法;- 底层用
std::malloc(sz)分配内存:成功则返回指针,失败则抛出std::bad_alloc异常(C++ 标准强制要求分配失败时抛出此异常); - 打印日志:输出调用时机和分配的内存大小(比如
new int会传入sz=4,因为int占 4 字节)。
三、重载数组版 operator new[](对应 new 数组)
// no inline, required by [replacement.functions]/3
void* operator new[](std::size_t sz)
{
std::printf("2) new[](size_t), size = %zu\n", sz);
if (sz == 0)
++sz; // avoid std::malloc(0) which may return nullptr on success
if (void *ptr = std::malloc(sz))
return ptr;
throw std::bad_alloc{}; // required by [new.delete.single]/3
}
- 核心作用 :替换全局的 "数组内存分配函数",
new int[10]、new MyClass[5]等操作会调用这个函数; - 关键细节 :逻辑和普通版几乎一致,唯一区别是日志标注为
2) new[],便于区分数组分配和单个对象分配; - 注意:
sz是数组的总字节数 (比如new int[10]会传入sz=4*10=40,某些编译器可能额外加几个字节存储数组长度,但这段代码里是标准的 40 字节)。
四、重载普通版 operator delete(对应 delete 单个对象)
// 版本1:仅接收指针
void operator delete(void* ptr) noexcept
{
std::puts("3) delete(void*)");
std::free(ptr);
}
// 版本2:接收指针 + 大小(C++14 起支持)
void operator delete(void* ptr, std::size_t size) noexcept
{
std::printf("4) delete(void*, size_t), size = %zu\n", size);
std::free(ptr);
}
- 核心作用 :替换全局的 "单个对象内存释放函数",
delete p1会调用这两个版本中的一个(编译器决定); - 关键细节 :
noexcept:析构函数 / 释放函数不能抛出异常,标准强制要求operator delete标记为noexcept;- 两个版本的区别:
- 版本 1(仅指针):通用版本,任何
delete 单个对象都可以调用; - 版本 2(指针 + 大小):C++14 新增,编译器如果知道释放的内存大小,会优先调用这个版本(减少内存开销);
- 版本 1(仅指针):通用版本,任何
- 底层用
std::free(ptr)释放内存:和malloc配对,归还内存给系统。
五、重载数组版 operator delete[](对应 delete[] 数组)
// 版本1:仅接收指针
void operator delete[](void* ptr) noexcept
{
std::puts("5) delete[](void* ptr)");
std::free(ptr);
}
// 版本2:接收指针 + 大小(C++14 起支持)
void operator delete[](void* ptr, std::size_t size) noexcept
{
std::printf("6) delete[](void*, size_t), size = %zu\n", size);
std::free(ptr);
}
- 核心作用 :替换全局的 "数组内存释放函数",
delete[] p2会调用这两个版本中的一个; - 关键细节 :逻辑和普通版
operator delete一致,日志标注为5)/6) delete[],便于区分数组释放和单个对象释放。
六、主函数:测试重载函数的调用
int main()
{
int* p1 = new int; // 调用 1) operator new(size_t),sz=4
delete p1; // 调用 3) 或 4) operator delete
int* p2 = new int[10]; // 调用 2) operator new[](size_t),sz=40
delete[] p2; // 调用 5) 或 6) operator delete[]
}
-
执行流程与预期输出 (不同编译器可能略有差异,比如
delete调用版本 1 还是版本 2):plaintext
1) new(size_t), size = 4 // new int 调用普通版 new,sz=4(int占4字节) 3) delete(void*) // delete p1 调用普通版 delete(仅指针版本) 2) new[](size_t), size = 40 // new int[10] 调用数组版 new[],sz=4*10=40 5) delete[](void* ptr) // delete[] p2 调用数组版 delete[](仅指针版本)(如果编译器支持并调用带大小的 delete 版本,输出会是 4) 或 6),比如:
4) delete(void*, size_t), size = 4、6) delete[](void*, size_t), size = 40)。
关键补充(为什么要重载这些函数?)
这段代码是典型的 "追踪内存分配" 场景,实际开发中重载全局 operator new/delete 的常见用途:
- 内存泄漏检测:在重载函数中记录所有分配 / 释放的指针,程序退出时检查是否有未释放的指针;
- 内存池集成 :把底层的
malloc/free替换成自定义内存池的申请 / 释放函数,提升性能; - 内存分配统计:统计程序运行过程中内存的总分配量、峰值使用量等;
- 调试内存问题:打印分配 / 释放的调用栈,定位非法内存操作(比如重复释放、野指针)。
总结
- 这段代码重载了全局的
operator new/new[]/delete/delete[],替换了 C++ 默认的内存分配 / 释放逻辑; - 重载函数底层用
malloc/free实现,同时添加日志追踪调用时机和内存大小; - 严格遵循 C++ 标准约束(如处理
sz=0、抛出bad_alloc、noexcept等); - 主函数通过
new int/new int[10]测试重载函数,直观展示了new/delete关键字与底层operator new/delete的对应关系。
全局 operator new 就是 C++ 对 C 语言 malloc 的「面向对象封装」,加上了异常、sz=0 处理等 C++ 特有的约束;operator delete 则是对 free 的封装。