operator new/delete

重载了 C++ 全局的 operator new/operator delete(包括普通版和数组版) ,并通过打印日志的方式,直观展示这些底层内存分配 / 释放函数的调用时机和参数,帮你看清 new/delete 关键字背后的实际执行逻辑。

整体功能总结

这段代码的核心目的是:

  1. 重载全局作用域的 operator new/operator new[](内存分配)和 operator delete/operator delete[](内存释放);
  2. 在重载函数中添加打印日志,追踪 new intnew int[10] 等操作实际调用的底层函数,以及传递的参数(分配 / 释放的内存大小);
  3. 严格遵循 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 intnew MyClass 等操作会调用这个函数;
  • 关键细节
    1. no inline 约束:C++ 标准要求重载的全局 operator new 不能是内联函数(否则无法正确替换默认版本);
    2. sz == 0 处理:std::malloc(0) 的行为是未定义的(可能返回 nullptr),因此强制把 sz=0 改成 1,保证 malloc 调用合法;
    3. 底层用 std::malloc(sz) 分配内存:成功则返回指针,失败则抛出 std::bad_alloc 异常(C++ 标准强制要求分配失败时抛出此异常);
    4. 打印日志:输出调用时机和分配的内存大小(比如 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 会调用这两个版本中的一个(编译器决定);
  • 关键细节
    1. noexcept:析构函数 / 释放函数不能抛出异常,标准强制要求 operator delete 标记为 noexcept
    2. 两个版本的区别:
      • 版本 1(仅指针):通用版本,任何 delete 单个对象 都可以调用;
      • 版本 2(指针 + 大小):C++14 新增,编译器如果知道释放的内存大小,会优先调用这个版本(减少内存开销);
    3. 底层用 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 = 46) delete[](void*, size_t), size = 40)。

关键补充(为什么要重载这些函数?)

这段代码是典型的 "追踪内存分配" 场景,实际开发中重载全局 operator new/delete 的常见用途:

  1. 内存泄漏检测:在重载函数中记录所有分配 / 释放的指针,程序退出时检查是否有未释放的指针;
  2. 内存池集成 :把底层的 malloc/free 替换成自定义内存池的申请 / 释放函数,提升性能;
  3. 内存分配统计:统计程序运行过程中内存的总分配量、峰值使用量等;
  4. 调试内存问题:打印分配 / 释放的调用栈,定位非法内存操作(比如重复释放、野指针)。

总结

  1. 这段代码重载了全局的 operator new/new[]/delete/delete[],替换了 C++ 默认的内存分配 / 释放逻辑;
  2. 重载函数底层用 malloc/free 实现,同时添加日志追踪调用时机和内存大小;
  3. 严格遵循 C++ 标准约束(如处理 sz=0、抛出 bad_allocnoexcept 等);
  4. 主函数通过 new int/new int[10] 测试重载函数,直观展示了 new/delete 关键字与底层 operator new/delete 的对应关系。

全局 operator new 就是 C++ 对 C 语言 malloc 的「面向对象封装」,加上了异常、sz=0 处理等 C++ 特有的约束;operator delete 则是对 free 的封装

相关推荐
superman超哥1 天前
Rust `‘static` 生命周期:从字面意义到深层语义
开发语言·后端·rust·生命周期·编程语言·rust static·深层语义
平生不喜凡桃李1 天前
Google C++ Style Guide : 变量与函数名
开发语言·c++·google c++
yaoxin5211231 天前
285. Java Stream API - 通过 Supplier 创建 Stream
java·开发语言
hweiyu001 天前
二分图匹配算法:匈牙利算法
算法
HL_风神1 天前
设计原则之单一职责原则
c++·学习·设计模式·单一职责原则
IAR Systems1 天前
在IAR Embedded Workbench for Renesas RH850中实现ROPI
linux·运维·算法
搂着猫睡的小鱼鱼1 天前
基于Python的淘宝评论爬虫
开发语言·爬虫·python
这里是彪彪1 天前
Java多线程中的单例模式
java·开发语言·单例模式
linzihahaha1 天前
C++ 单例模式总结
开发语言·c++·单例模式