当前位置: 首页 > backend >正文

C++ 动态内存管理详讲

1. 四个全局函数的定义与作用

这四个函数只负责空间的开辟和释放,不会调构造和析构

(1) ::operator new

cpp

void* operator new(size_t size);  // 全局版本
  • 功能:分配 size 字节的未初始化内存。

  • 底层实现:调用 malloc(size)

  • 调用场景

    • 直接调用:void* p = ::operator new(100);(手动分配 100 字节)。

    • 间接调用:new T 会先调用 operator new(sizeof(T)),然后调T构造函数初始化成员变量

(2) ::operator delete

cpp

void operator delete(void* ptr) noexcept;  // 全局版本
  • 功能:释放 operator new 分配的内存。

  • 底层实现:调用 free(ptr)

  • 调用场景

    • 直接调用:::operator delete(p),释放空间;

    • 间接调用:delete p 会先调用析构函数释放资源内存空间,再调用 operator delete(p)释放对象本身空间

(3) ::operator new[]

cpp

void* operator new[](size_t size);  // 全局版本
  • 功能:分配 size 字节的未初始化内存(用于数组)。

  • 底层实现:封装调用 operator new(size),更底层就是malloc

  • 调用场景

    • 直接调用:void* p = ::operator new[](100);

    • 间接调用:new T[n] 会先调用 operator new[](n * sizeof(T) + 额外空间),然后调用n次构造函数

(4) ::operator delete[]

cpp

void operator delete[](void* ptr) noexcept;  // 全局版本
  • 功能:释放 operator new[] 分配的内存。

  • 底层实现:调用 operator delete(ptr)(即 free)。

  • 调用场景

    • 直接调用:::operator delete[](p);

    • 间接调用:delete[] p 会先调用n次析构函数释放资源,再调用 operator delete[](p)释放整块对象本身空间


2. 与 new/delete 的关系

操作底层调用额外行为
new T::operator new(sizeof(T))调用构造函数
delete p::operator delete(p)先调用析构函数
new T[n]::operator new[](n * sizeof(T) + 额外)调用 n 次构造函数
delete[] p::operator delete[](p)先调用 n 次析构函数

关键区别

  • operator new/delete 只负责 内存分配/释放,不涉及构造/析构。

  • new/delete 是 更高层的操作,会额外调用构造/析构函数。

3. 为什么需要额外空间?

当使用 new T[n] 时:

  • 如果 T 是内置类型(如 intchar在释放时只需要释放内存,无需调用析构函数,因此开辟空间时可能不需要额外空间

  • 如果 T 是自定义类型(有析构函数)delete[] 必须知道数组大小 n,才能正确调用 n 次析构函数。

问题delete[] 如何知道 n
答案:编译器会在 operator new[] 分配的内存中隐式插入 n,通常是存储在返回指针之前的额外空间里。

4. 额外空间的存储方式

(1) 典型的内存布局

[对象数量 n][T 对象 0][T 对象 1]...[T 对象 n-1]↑p(返回给用户的指针)
  • p 是用户得到的指针,指向第一个对象 T[0]

  • p - sizeof(size_t) 的位置可能存储 n(具体偏移量由编译器决定)。

(2) 分配的总大小

operator new[] 实际分配的内存大小:

cpp

总大小 = sizeof(T) * n + 额外空间(存储 n 或其他元数据)=sz(传参) + sizeof(n)  
  • 额外空间的大小

    • 可能是 sizeof(size_t)(存储 n)。


5. delete[] 如何利用额外空间?

当调用 delete[] p 时:

  1. 定位 n

    • 编译器生成代码,从 p - offset 处读取 noffset 是编译器决定的,通常是 sizeof(size_t))。

  2. 调用析构函数

    • 逆序调用 n 次析构函数(从 p[n-1] 到 p[0])。

  3. 释放内存

    • 调用 operator delete[](original_ptr),其中 original_ptr 是 operator new[] 返回的原始指针(比 p 小,指向分配块的起始地址)。


6. 关键问题解答

(1) 为什么 new[] 和 delete[] 必须配对使用?

  • new[] 可能分配额外空间存储 n,而 delete[] 依赖这个信息调用析构函数。

(2) 如何手动模拟 new[] 和 delete[]

cpp

// 手动实现 new[]
void* operator new[](size_t size) {size_t total_size = size + sizeof(size_t);  // 额外空间存 nvoid* ptr = malloc(total_size);*(size_t*)ptr = size / sizeof(T);  // 存储 nreturn (char*)ptr + sizeof(size_t);  // 返回用户指针
}// 手动实现 delete[]
void operator delete[](void* p) noexcept {size_t* hidden_n = (size_t*)p - 1;size_t n = *hidden_n;for (size_t i = n; i > 0; --i) {((T*)p)[i - 1].~T();  // 调用析构函数}free(hidden_n);  // 释放原始指针
}

关键字

1. new 和 delete 的行为

(1) new 的流程(T* p = new T(args);
  1. 调用 operator new(sizeof(T)) 开辟空间。

  2. 在分配的内存上调用 T 的构造函数初始化成员变量(如果是内置类型,无构造步骤)。

  3. 返回指向对象的指针。

(2) delete 的流程(delete p;
  1. 调用 p 指向对象的析构函数释放资源空间(如果是内置类型,无析构步骤)。

  2. 调用 operator delete(p) 释放对象本身空间。


2. new[] 和 delete[] 的行为

(1) new[] 的流程(T* p = new T[n];
  1. 调用 operator new[](sizeof(T) * n) 分配内存。

    • 额外空间可能存储对象数量(编译器实现相关)。

  2. 对每个元素依次调用构造函数初始化成员变量(从 p[0] 到 p[n-1])。

(2) delete[] 的流程(delete[] p;
  1. 对每个元素逆序调用析构函数释放资源空间(从 p[n-1] 到 p[0])。

  2. 调用 operator delete[](p) 释放对象本身空间。

http://www.xdnf.cn/news/3680.html

相关文章:

  • 【Java IO流】字节输入流FileInputStream、字节输出流FileOutputStream
  • ICRA 2025 基于触觉反馈的闭环分层控制框架——开放环境下通用门开启的智能规划与操作
  • 【unity游戏开发入门到精通——UGUI】实现精准点击异形或者不规则图片button按钮
  • 字符串的相关方法
  • 【黑马JavaWeb+AI知识梳理】后端Web基础02 - Web基础
  • 街景主观感知全流程(自建数据集+两两对比程序+Trueskill计算评分代码+训练模型+大规模预测)20
  • Winform(8.常用控件1)
  • 电商平台的订单状态设计流程
  • QT中的QSS---界面美化
  • 时间给了我们什么?
  • 本地服务验证-仙盟创梦IDE-智能编程,编程自动备份+编程审计
  • C++开发指南
  • MyBatis 参数处理全解析
  • AI大模型-RAG到底能做些什么?
  • 变色龙-第16届蓝桥第5次STEMA测评Scratch真题第1题
  • 52、【OS】【Nuttx】【OSTest】setvbuf 测试
  • 正态分布全景解析:理论、推导与应用
  • Linux-sysctl工具解析
  • 《AI大模型应知应会100篇》第44篇:大模型API调用最佳实践(附完整代码模板)
  • GC9D01 和 GC9A01两种TFT 液晶显示驱动芯片
  • Set的局限性
  • C#将Mat或Byte快速转换为Bitmap格式
  • 组件通信-provide、inject
  • maven install时报错:【无效的目标发行版: 17】
  • 多模态大模型轻量化探索-视觉大模型SAM(Segment Anything Model)
  • C++11新特性_标准库_智能指针_std::weak_ptr
  • MATLAB技巧——norm和vecnorm两个函数讲解与辨析
  • Linux的环境变量
  • “会话技术”——Cookie_(2/2)原理与使用细节
  • [更新完毕]2025五一杯C题五一杯数学建模思路代码文章教学:社交媒体平台用户分析问题