C & C++ 内存管理


  • 点击表格内对应链接跳转对应内容⬇️⬇️⬇️
作者主页 吃透C语言专栏 数据结构 Gitee仓库 C++ Linux 算法

文章目录

  • 一,C/C++内存分布
  • 二,C语言动态内存管理方式
  • 三,C++内存管理方式
    • [1 new和delete操作内置类型](#1 new和delete操作内置类型)
    • [2 new 和 delete操作自定义类型](#2 new 和 delete操作自定义类型)
  • [四,operator new 和 operator delete 函数](#四,operator new 和 operator delete 函数)
    • [1 operator new函数](#1 operator new函数)
    • [2 operator delete 函数](#2 operator delete 函数)
  • [五,new 和 delete的底层原理](#五,new 和 delete的底层原理)
    • [1 new 对于内置类型底层原理](#1 new 对于内置类型底层原理)
    • [2 delete对于内置类型底层原理](#2 delete对于内置类型底层原理)
    • [3 new 对于自定义类型底层原理](#3 new 对于自定义类型底层原理)
    • [4 new T 的原理](#4 new T[ ]的原理)
    • [5 delete 对于自定义类型底层原理](#5 delete 对于自定义类型底层原理)
    • [6 delete 的原理](#6 delete[ ]的原理)
  • 六,定位new表达式(placement-new)
  • [七,newT 和 delete 匹配问题](#七,newT[ ] 和 delete[ ]匹配问题)

一,C/C++内存分布


内存分区 作用 增长方向
栈(堆栈) 非静态局部变量 / 函数参数 / 返回值等 向下增长
内存映射段 高效的 I/O 映射方式,用于装载共享的动态内存库;用户可使用系统接口创建共享内存,做进程间通信 ---
堆 程序运行时动态内存分配 向上增长
数据段 存储全局数据和静态数据 ---
代码段 可执行的代码 / 只读常量 ---

二,C语言动态内存管理方式

  • C语言的内存管理函数在C++中照样可以用,C++兼容C
  • 点击博客链接跳转->动态内存管理

三,C++内存管理方式

  • new和delete 是C++ 运算符 ,用来在堆(heap)上动态申请、释放内存;底层封装了 malloc/free,并且自动调用构造函数、析构函数 (这是和 C 语言malloc/free最大区别)

1 new和delete操作内置类型

c 复制代码
int main()
{
    int* ptr1 = new int;//没初始化,随机值
    
    int* ptr2 = new int(10);//初始化成10

    int* ptr3 = new int[10];//申请10个iptnt类型空间

    int* ptr4 = new int[10] {1, 2, 3, 4, 5, 6, 7, 8, 9,10};//初始化

    std::cout << *ptr1 << std::endl;
    std::cout << *ptr2 << std::endl;
    std::cout << *ptr3 << std::endl;
    for (int i = 0; i <= 9; i++)
    {
        std::cout << *(ptr4 + i) << " ";
    }

    delete ptr1;
    delete ptr2;
    delete[]ptr3;
    delete[]ptr4;

    return 0;
}
  • new对于内置类型,和malloc对于内置类型的效果基本一致,只是写的形式不同而已。如果内置类型不初始化为随机值
序号 new要点 解析
1️⃣ int* ptr1 = new int new+类型就可以在堆区申请对应类型的空间
2️⃣ int* ptr2 = new int(10) 空间初始化则()里面初始化,()一般用来传值
3️⃣ int* ptr3 = new int[10] new + 类型 + [个数]就可以按照该类型开辟多个连续空间
4️⃣ int* ptr4 = new int[10]{1,2,4,5,,6,7,8,9,10} { }内就可以给开辟的多个空间初始化

  • 总结:new 后面跟着内置类型就可以开辟对应大小的空间,但是不会初始化,如果要初始化就西要( )手动传值,如果要开辟多个空间,[ ]内写上数量即可(和数组写法一样),如果开辟多个空间也要初始化的话,{ }内初始化即可(和数组一样写法)

  • delete + 地址,就可以释放对于的空间,对于内置类型,和free的效果一样,所以释放以后,最好把指针置为nullptr,delete对于内置类型,只会释放对应的空间,但是不会对指针置空

序号 delete要点 解析
1️⃣ delete ptr1 释放一个空间delete后面加对应地址即可
2️⃣ delete [ ]ptr2 释放多个只要在delete 和 地址中间加个[ ]即可,编译器会自动通过new中[ ]的数量匹配上
---``
  • 总结:对于内置类型,new和delete与malloc和free的效果是一样的都是连续的空间,都不会自动初始化,因为new和delete的底层调用的就是malloc和free,是对malloc和free的封装,下面会有更加详细的解析


2 new 和 delete操作自定义类型

名称 类别 执行顺序 / 功能 说明
new new表达式(操作符) ①调用operator new分配原始内存 → ②调用默认构造函数 完整创建对象,分配内存+初始化对象
delete delete表达式(操作符) ①调用析构函数 → ②调用operator delete释放内存 完整销毁对象,清理资源+归还内存
new[] / delete[] 数组版new/delete表达式 new :分配整块内存,逐个调用默认构造 delete :逐个调用析构,再释放整块内存 对数组里每一个自定义类型元素分别构造、析构
operator new / operator delete 全局/类静态函数 仅内存分配 / 内存释放 底层内存函数,不负责构造和析构,new表达式底层会调用它

✅ 关键区分:

new / delete 是表达式(操作符) ;

operator new / operator delete 只是内存函数,不是操作符。


  • 总结:new 对于自定义类型,不仅会分配内存空间,还有自动调用自定义类型对应的构造函数
  • 总结:delete对于自定义类型,在释放内存之前,还会调用自定义类型对应的析构函数

c 复制代码
class A
{
public:
	A()
		:_x(10)
	{
		std::cout << "A()" << std::endl;
	}
	A(const int& x)
		:_x(x)
	{
		std::cout << "A(const int& x)" << std::endl;
	}
	~A()
	{    
		std::cout << "~A()" << std::endl;
	}

private:
	int _x;
};


int main()
{
	A* ptr1 = new A;
	A* ptr2 = new A(100);
	delete ptr1;
	delete ptr2;
}
  • new + 类名则直接创建A类的空间,但是和malloc不同的地方是,new给自定义类型向堆区申请空间会自动调用该自定义类型的默认构造函数,如果是带参构造的话,则需要new + 类名(传参),在()内传参给构造函数

  • delete + 地址对应自定义类型则会先调用析构函数,然后再去释放回收自定义类型的空间

c 复制代码
class A
{
public:
	A()
		:_x(10)
	{
		std::cout << "A()" << std::endl;
	}
	A(const int& x)
		:_x(x)
	{
		std::cout << "A(const int& x)" << std::endl;
	}
	~A()
	{    
		std::cout << "~A()" << std::endl;
	}

private:
	int _x;
};


int main()
{
	A* ptr1 = new A[3];
	A* ptr2 = new A[4]{ 1,2,3,4 };
	delete []ptr1;
	delete []ptr2;
}
  • new + 类型 +[数量]就可以连续开辟多个同类的空间,和数组差不多的使用方法,如果要对自定义类型赋值的话,{ }内传即可,申请多少个该类型的空间,就调用多少次默认构造

  • A* ptr2 = new A [ 4 ]{ 1 , 2 , 3 , 4 } 中{ }内应该传的是A类同类对象,因为给A类的空间初始化,自然要同类对象拷贝到A类空间中,但是直接写{1,2,3,4}的话,A类的空间,需要同类对象赋值,拷贝进去,而写了{1,2,3,4}则会触发隐式类型转换,需要A类对象,则将{1,2,3,4}作为参数传给A类单参构造函数,构造成A类临时对象,这样就满足了{ }内必须是同类对象的要求,直接把内置类型当作单参构造的参数创建同类临时对象

  • delete [ ] 地址就可以释放地址对应自定义类型的空间,但是会先调用该自定义类型的析构函数,有多少个该自定义类型,就调用多少次析构

  • 补充:❗❗❗有realloc但是没有realnew,realloc是扩容使用的,但是要用new扩容的话,必须手动拷贝原来空间的值,再释放原来的空间,再用new申请一块新的空间将拷贝的值放进去,手动实现realloc的功能

四,operator new 和 operator delete 函数

  • operator new是内存分配函数 同时 operator delete是内存释放函数 ,它们只负责"拿内存"和"还内存",不调用构造函数和析构函数

1 operator new函数

  • 只负责向堆申请一块原始的、未初始化的裸内存,不调用构造函数

  • 全局原型⬇️
c 复制代码
// 全局原型
void* operator new(size_t size);

  • 底层实现(简化版)⬇️
c 复制代码
void* operator new(size_t size)
{
    void* ptr = malloc(size); // 底层封装malloc去操作系统申请堆内存
    if (ptr == nullptr)
    {
        throw bad_alloc(); // 分配失败抛异常
    }
    return ptr;
}

项目 内容
参数 size_t size:需要分配多少字节内存(编译器自动传入类的大小 sizeof(类))
返回值 分配好内存的首地址;分配失败默认抛出 std::bad_alloc 异常(C++标准)
功能 ≈ malloc,但是失败行为不同:malloc 返回 nullptr,默认 operator new 抛异常

  • 总结:operator new函数底层其实是对malloc的封装,底层靠的还是malloc申请堆空间,和malloc不同的是,malloc失败返回nullptr,而operator new失败是抛异常

2 operator delete 函数

  • 只负责把内存归还操作系统,释放堆内存,不调用析构函数

  • 全局原型
c 复制代码
// 全局原型
void operator delete(void* ptr) noexcept;

  • 底层实现(简化版)⬇️
c 复制代码
void operator delete(void* ptr) noexcept
{
    if(ptr != nullptr)
    {
        free(ptr);
    }
}

项目 内容
参数 待释放内存的首地址
返回值 无返回值;底层封装 free
特性 不会自动调用析构函数,析构是 delete 表达式提前做的事情。

  • 总结:operator delete底层其实是对free的封装,底层还是靠的还是free释放堆空间,所以功能和free几乎一致,因为底层就是free

  • 总结:operator new 底层封装的是malloc,靠的就是malloc,不同在于malloc失败返回nullptr而operator new失败是抛异常,operator delete 底层封装的就是free,靠的就是free

五,new 和 delete的底层原理

1 new 对于内置类型底层原理

  • 对于内置类型:new的底层调用operator new,而operator new 底层封装的是malloc,对于内置类型,new最后还是靠的是malloc申请空间,所以这就是为什么new对于内置类型的效果和malloc的效果几乎一样的原因,不初始化,但是区别在于malloc申请堆空间失败返回的是nullptr,但是new申请空间失败是抛异常,对于内置类型new底层调用的是operator new 所以operator new 申请堆空间失败也是抛异常。

对于内置类型:new(抛异常) ---调用---> operator new(抛异常) ---封装--->malloc(返回nullptr)


2 delete对于内置类型底层原理

  • 对于内置类型:delete底层调用的是operator delete,而delete底层封装的是free,所以对于内置类型,delete调用的依然是free,所以对于内置类型,delete的效果和free基本一样的原因就是这个

  • 对于内置类型:delete --调用--> operator delete --封装--> free

3 new 对于自定义类型底层原理

步骤 操作
1️⃣ 调用operator new函数申请空间
2️⃣ 在申请的空间上执行默认构造函数,完成对象的构造

  • 对于自定义类型,new和malloc的区别有1:new失败抛引用,malloc失败返回nullptr 和 2:new会调用默认构造

  • 解析:new对于自定义类型,比对于内置类型多了调用默认构造这一个步骤。

c 复制代码
A* p = new A;
步骤 动作 细节
1️⃣ 调用全局函数 operator new(sizeof(A)) 默认 operator new 底层封装 malloc,向堆申请一块原始裸内存 (未初始化,不是对象);如果 malloc 分配失败,operator new 会抛出 std::bad_alloc 异常(malloc 只会返回 nullptr)
2️⃣ 定位 new(placement new) 在刚分配好的内存上调用 A 的构造函数,初始化成员,把裸内存变成合法对象
重点 ❗❗❗ operator new 只分配内存,不会调用构造函数 !构造是定位 new 干的

  • 总结:对于自定义类型,new的第一步是调用operator new函数向内存堆区申请空间,而 operator new 底层封装的是malloc,区别在于operator new 失败抛引用,第二步则是调用定位new(placement new),定位new的作用就是在分配好的自定义类型内存上调用构造函数,对空间operator new 申请的堆空间初始化

#mermaid-svg-nnCdsM2AuIgE5qlz{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-nnCdsM2AuIgE5qlz .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-nnCdsM2AuIgE5qlz .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-nnCdsM2AuIgE5qlz .error-icon{fill:#552222;}#mermaid-svg-nnCdsM2AuIgE5qlz .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-nnCdsM2AuIgE5qlz .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-nnCdsM2AuIgE5qlz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-nnCdsM2AuIgE5qlz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-nnCdsM2AuIgE5qlz .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-nnCdsM2AuIgE5qlz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-nnCdsM2AuIgE5qlz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-nnCdsM2AuIgE5qlz .marker{fill:#333333;stroke:#333333;}#mermaid-svg-nnCdsM2AuIgE5qlz .marker.cross{stroke:#333333;}#mermaid-svg-nnCdsM2AuIgE5qlz svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-nnCdsM2AuIgE5qlz p{margin:0;}#mermaid-svg-nnCdsM2AuIgE5qlz .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-nnCdsM2AuIgE5qlz .cluster-label text{fill:#333;}#mermaid-svg-nnCdsM2AuIgE5qlz .cluster-label span{color:#333;}#mermaid-svg-nnCdsM2AuIgE5qlz .cluster-label span p{background-color:transparent;}#mermaid-svg-nnCdsM2AuIgE5qlz .label text,#mermaid-svg-nnCdsM2AuIgE5qlz span{fill:#333;color:#333;}#mermaid-svg-nnCdsM2AuIgE5qlz .node rect,#mermaid-svg-nnCdsM2AuIgE5qlz .node circle,#mermaid-svg-nnCdsM2AuIgE5qlz .node ellipse,#mermaid-svg-nnCdsM2AuIgE5qlz .node polygon,#mermaid-svg-nnCdsM2AuIgE5qlz .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-nnCdsM2AuIgE5qlz .rough-node .label text,#mermaid-svg-nnCdsM2AuIgE5qlz .node .label text,#mermaid-svg-nnCdsM2AuIgE5qlz .image-shape .label,#mermaid-svg-nnCdsM2AuIgE5qlz .icon-shape .label{text-anchor:middle;}#mermaid-svg-nnCdsM2AuIgE5qlz .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-nnCdsM2AuIgE5qlz .rough-node .label,#mermaid-svg-nnCdsM2AuIgE5qlz .node .label,#mermaid-svg-nnCdsM2AuIgE5qlz .image-shape .label,#mermaid-svg-nnCdsM2AuIgE5qlz .icon-shape .label{text-align:center;}#mermaid-svg-nnCdsM2AuIgE5qlz .node.clickable{cursor:pointer;}#mermaid-svg-nnCdsM2AuIgE5qlz .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-nnCdsM2AuIgE5qlz .arrowheadPath{fill:#333333;}#mermaid-svg-nnCdsM2AuIgE5qlz .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-nnCdsM2AuIgE5qlz .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-nnCdsM2AuIgE5qlz .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nnCdsM2AuIgE5qlz .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-nnCdsM2AuIgE5qlz .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nnCdsM2AuIgE5qlz .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-nnCdsM2AuIgE5qlz .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-nnCdsM2AuIgE5qlz .cluster text{fill:#333;}#mermaid-svg-nnCdsM2AuIgE5qlz .cluster span{color:#333;}#mermaid-svg-nnCdsM2AuIgE5qlz div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-nnCdsM2AuIgE5qlz .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-nnCdsM2AuIgE5qlz rect.text{fill:none;stroke-width:0;}#mermaid-svg-nnCdsM2AuIgE5qlz .icon-shape,#mermaid-svg-nnCdsM2AuIgE5qlz .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nnCdsM2AuIgE5qlz .icon-shape p,#mermaid-svg-nnCdsM2AuIgE5qlz .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-nnCdsM2AuIgE5qlz .icon-shape .label rect,#mermaid-svg-nnCdsM2AuIgE5qlz .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nnCdsM2AuIgE5qlz .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-nnCdsM2AuIgE5qlz .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-nnCdsM2AuIgE5qlz :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 第一步调用
底层
第二步调用
功能
new
operator new函数
封装malloc, 但是失败抛异常
new(placement new)定位new表达式
在内存上调用自定义类型的构造函数


4 new T 的原理

步骤 动作
1️⃣ 调用 operator new[] 函数,在 operator new[] 中实际调用 operator new 函数完成 N 个对象空间的申请
2️⃣ 在申请的空间上执行 N 次构造函数

c 复制代码
A* arr = new A[5];
newT 说明
1️⃣ 调用 operator new[] 一次性分配一整块内存,额外多存一个隐藏数字(记录对象数量 5)
2️⃣ 循环 5 次,逐个调用构造函数

  • 总结:new T[ ]和new本身就是一致的,区别就在于newT[ ]一次性开多个的空间,所以写上需要开的数量,直接一次性分配好,但是对于第二步骤,定位new表达式通过调用构造初始化内存的话,会根据自定义类型的数量循环逐个调用构造函数

5 delete 对于自定义类型底层原理

步骤 操作
1️⃣ 在空间上执行析构函数,完成对象中资源的清理工作
2️⃣ 调用operator delete函数释放对象的空间

  • ❗❗❗注意:顺序不能乱,一定是先在开辟好的内存空间上,调用析构清理资源,第二步才是调用operator delete 函数释放内存空间

c 复制代码
delete p;
delete 执行流程 说明
编译器同样展开成 2 步,顺序不能颠倒 ---
1️⃣ 调用对象的析构函数 p->~A() 清理对象内部资源(比如类里面 new 出来的成员)
2️⃣ 调用 operator delete(p) 默认 operator delete 底层封装 free,把内存还给堆
重点 operator delete 只释放内存,不会调用析构函数 !析构是 delete 表达式第一步完成的

  • 总结:对于自定义类型,delete有两个步骤,顺序不能颠倒,第一步调用自定义类型自己的析构函数,对自定义类型中的资源进行清理,比如A类空间中,有一个指针变量接收的new的空间,析构函数就是清理指针变量的指向的空间,以及清理指针变量的,而第二步才是调用operator delete 释放自定义类型的内存空间,operator delete 底层封装的是free

#mermaid-svg-x6LzvCpni0TmQwpH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-x6LzvCpni0TmQwpH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-x6LzvCpni0TmQwpH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-x6LzvCpni0TmQwpH .error-icon{fill:#552222;}#mermaid-svg-x6LzvCpni0TmQwpH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-x6LzvCpni0TmQwpH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-x6LzvCpni0TmQwpH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-x6LzvCpni0TmQwpH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-x6LzvCpni0TmQwpH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-x6LzvCpni0TmQwpH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-x6LzvCpni0TmQwpH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-x6LzvCpni0TmQwpH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-x6LzvCpni0TmQwpH .marker.cross{stroke:#333333;}#mermaid-svg-x6LzvCpni0TmQwpH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-x6LzvCpni0TmQwpH p{margin:0;}#mermaid-svg-x6LzvCpni0TmQwpH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-x6LzvCpni0TmQwpH .cluster-label text{fill:#333;}#mermaid-svg-x6LzvCpni0TmQwpH .cluster-label span{color:#333;}#mermaid-svg-x6LzvCpni0TmQwpH .cluster-label span p{background-color:transparent;}#mermaid-svg-x6LzvCpni0TmQwpH .label text,#mermaid-svg-x6LzvCpni0TmQwpH span{fill:#333;color:#333;}#mermaid-svg-x6LzvCpni0TmQwpH .node rect,#mermaid-svg-x6LzvCpni0TmQwpH .node circle,#mermaid-svg-x6LzvCpni0TmQwpH .node ellipse,#mermaid-svg-x6LzvCpni0TmQwpH .node polygon,#mermaid-svg-x6LzvCpni0TmQwpH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-x6LzvCpni0TmQwpH .rough-node .label text,#mermaid-svg-x6LzvCpni0TmQwpH .node .label text,#mermaid-svg-x6LzvCpni0TmQwpH .image-shape .label,#mermaid-svg-x6LzvCpni0TmQwpH .icon-shape .label{text-anchor:middle;}#mermaid-svg-x6LzvCpni0TmQwpH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-x6LzvCpni0TmQwpH .rough-node .label,#mermaid-svg-x6LzvCpni0TmQwpH .node .label,#mermaid-svg-x6LzvCpni0TmQwpH .image-shape .label,#mermaid-svg-x6LzvCpni0TmQwpH .icon-shape .label{text-align:center;}#mermaid-svg-x6LzvCpni0TmQwpH .node.clickable{cursor:pointer;}#mermaid-svg-x6LzvCpni0TmQwpH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-x6LzvCpni0TmQwpH .arrowheadPath{fill:#333333;}#mermaid-svg-x6LzvCpni0TmQwpH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-x6LzvCpni0TmQwpH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-x6LzvCpni0TmQwpH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-x6LzvCpni0TmQwpH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-x6LzvCpni0TmQwpH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-x6LzvCpni0TmQwpH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-x6LzvCpni0TmQwpH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-x6LzvCpni0TmQwpH .cluster text{fill:#333;}#mermaid-svg-x6LzvCpni0TmQwpH .cluster span{color:#333;}#mermaid-svg-x6LzvCpni0TmQwpH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-x6LzvCpni0TmQwpH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-x6LzvCpni0TmQwpH rect.text{fill:none;stroke-width:0;}#mermaid-svg-x6LzvCpni0TmQwpH .icon-shape,#mermaid-svg-x6LzvCpni0TmQwpH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-x6LzvCpni0TmQwpH .icon-shape p,#mermaid-svg-x6LzvCpni0TmQwpH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-x6LzvCpni0TmQwpH .icon-shape .label rect,#mermaid-svg-x6LzvCpni0TmQwpH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-x6LzvCpni0TmQwpH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-x6LzvCpni0TmQwpH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-x6LzvCpni0TmQwpH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 第一步调用
第二步调用
底层封装
delete
对象的析构函数( p ->~A() )
operator delete函数
free函数

6 delete 的原理

步骤 说明
1 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
2 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

c 复制代码
delete[] arr
delete\[\] 说明
1️⃣ 读取隐藏的计数,循环逐个调用析构函数
2️⃣ 调用 operator delete[ ] 一次性释放整块内存
为什么 new 必须配 delete delete[ ] 要靠隐藏计数,知道要调用多少次析构。混用会未定义行为

  • 总结:delete[ ]和delete是一样的,只不过delete[ ],会接收newT[ ]中自定义类型的个数,按照自定义类型的个数循环执行析构函数清理对象中的资源,最后调用operator delete[ ]一次性释放new[ ]申请的整块堆内存

六,定位new表达式(placement-new)

  • 定位new表达式(placement new):只在已经分配好的内存地址上调用构造函数

c 复制代码
class A
{
public:
	A(int a = 10)
		:_x(a)
	{
		std::cout << "A()" << std::endl;
	}
	~A()
	{
		std::cout << "~A()" << std::endl;
	}
private:
	int _x;
};

int main()
{
	A* ptr1 = (A*)malloc(sizeof(A));
	new(ptr1)A;
	ptr1->~A();
	free(ptr1);
	ptr1 = nullptr;

	A* ptr2 = (A*)operator new(sizeof(A));
	new(ptr2)A(20);
	ptr2->~A();
	operator delete(ptr2);
	ptr2 = nullptr;

	return 0;
}

  • 定位new表达式写法:new (指针) 类型(初始化参数),初始化参数就是给带参构造传参用的

  • ptr1解析:malloc申请的自定义类型空间,定位new表达式通过malloc返回的地址和类型,调用该类型对应的构造函数,没有ptr1->A()这种调构造的方式,因为定位new表达式就是对未初始化的类型空间地址调用类型对应的构造函数进行初始化的,new中调用的构造函数就是通过调用定位new表达式,定位new表达式通过类型来调用对应的构造函数的,所以调用构造函数需要通过定位new表达式,所以不能ptr1->A()这这样子显示调用构造函数,但是可以显示调用析构函数,比如ptr1->~A(),因为delete函数中,调用的析构函数就是delete自己调用的,所以可以显示调用析构,malloc开辟的空间free释放

  • ptr2解析:operaotr new底层调用的就是malloc,不同点在于operator new失败抛异常,malloc则是返回nullptr,new(ptr2)A(20)则是通过类型地址调用类型的带参构造函数,最后operator delete(ptr2)释放内存,底层调用的就是free,和operator new匹配

  • 总结:定位new表达式,在new中的作用是被调用来实现调用构造函数的功能的,和new对比,定位new表达式只有调用构造函数的功能,而new函数会一口气执行申请空间,构造的全流程,使用delete自动完成析构和释放空间,但是如果我们仅仅需要调用构造函数,需要手动调用p->~A()析构清理资源的话,就需要用到定位new表达式。
使用场景 说明 普通new能否实现
STL容器底层(vector) vector预分配capacity原始内存,不创建对象;push_back时用定位new在预留内存原地构造元素,pop_back仅调用析构,保留内存用于复用 ❌ 不能,普通new分配内存必须同时构造对象,无法预分配裸内存
实现内存池Memory Pool 一次性申请一大块连续内存,后续反复在这块内存上构造/销毁对象,减少频繁调用malloc/new带来的系统开销 ❌ 不能,普通new每次都单独申请堆内存,无法复用同一块内存
嵌入式/硬件固定地址构造对象 在硬件指定的物理地址、共享内存地址上直接构造C++对象,地址是硬件给定,不是动态分配出来的 ❌ 不能,普通new由操作系统随机分配堆地址,无法指定地址
内存复用(反复重建对象) 预先分配一块内存,对象析构后内存不释放,再次使用定位new原地重建新对象,避免重复分配释放 ❌ 普通new每次创建都要重新分配内存
对齐内存上构造对象 使用aligned_storage等获取满足对齐要求的裸内存,再通过定位new构造对象 ⚠️ 可以但麻烦,普通new自动对齐,但无法拿到裸内存延迟构造

c 复制代码
A* p1 = (A*)operator new(sizeof(A)*10)
new(p1)A[10]{1,2,3,4};
for(int i = 0;i<10;++i)
   new(p2 + i)A(i)
  • ⬆️相当于new A 10

c 复制代码
for(int i = 0;i<10;++i)
{
   (p2 + i)->~A();
}
operator delete[](p2)
  • ⬆️相当于delete

七,newT 和 delete 匹配问题

c 复制代码
int* p = new int[5];
  • 底层分配 5 * sizeof(int) = 20字节 + 4字节(记录元素数量 5 )= 24字节

数组元素的个数 count 说明
new 底层 先分配一块更大内存,最开头预留一块内存保存数组元素数量 ,然后返回的指针 p 指向元素数组起始位置(跳过前面这个计数)
关键结论 所以你拿到的指针,不是整块内存的起始地址,是偏移后的地址

delete p步骤 说明
① 向前偏移指针,拿到 count
② 循环 count 次,依次调用每个数组元素的析构函数
③ 释放整块内存(包含前面存 count 的那部分内存)
  • 总结:newT[ ]在内存中会额外开出一个空间存放[ ]count值,而int * p拿到的地址没包括这个count值,当调用delete[ ]的时候会自动往前挪到p1的位置开始读取count值,根据count值,循环调用析构函数。

不匹配 结果
newT[ ]和free或者delete 类 free和delete都无法前移读取到count值,无法完成全部析构,而且释放空间的地址应该在p1的位置,释放空间地址都是错误的
new类和delete[ ] new类根本就没有产生count值,delete不需要往前移动,但是调用的是delete[ ],依然会往前移动,去读取count值,最后就导致读取到的count值错误,越界访问,乱析构

  • 总结:必须匹配使用new就用delete,newT[ ]就用delete[ ],malloc就用free

#mermaid-svg-blauiltQaAqw6yik{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-blauiltQaAqw6yik .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-blauiltQaAqw6yik .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-blauiltQaAqw6yik .error-icon{fill:#552222;}#mermaid-svg-blauiltQaAqw6yik .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-blauiltQaAqw6yik .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-blauiltQaAqw6yik .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-blauiltQaAqw6yik .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-blauiltQaAqw6yik .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-blauiltQaAqw6yik .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-blauiltQaAqw6yik .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-blauiltQaAqw6yik .marker{fill:#333333;stroke:#333333;}#mermaid-svg-blauiltQaAqw6yik .marker.cross{stroke:#333333;}#mermaid-svg-blauiltQaAqw6yik svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-blauiltQaAqw6yik p{margin:0;}#mermaid-svg-blauiltQaAqw6yik .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-blauiltQaAqw6yik .cluster-label text{fill:#333;}#mermaid-svg-blauiltQaAqw6yik .cluster-label span{color:#333;}#mermaid-svg-blauiltQaAqw6yik .cluster-label span p{background-color:transparent;}#mermaid-svg-blauiltQaAqw6yik .label text,#mermaid-svg-blauiltQaAqw6yik span{fill:#333;color:#333;}#mermaid-svg-blauiltQaAqw6yik .node rect,#mermaid-svg-blauiltQaAqw6yik .node circle,#mermaid-svg-blauiltQaAqw6yik .node ellipse,#mermaid-svg-blauiltQaAqw6yik .node polygon,#mermaid-svg-blauiltQaAqw6yik .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-blauiltQaAqw6yik .rough-node .label text,#mermaid-svg-blauiltQaAqw6yik .node .label text,#mermaid-svg-blauiltQaAqw6yik .image-shape .label,#mermaid-svg-blauiltQaAqw6yik .icon-shape .label{text-anchor:middle;}#mermaid-svg-blauiltQaAqw6yik .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-blauiltQaAqw6yik .rough-node .label,#mermaid-svg-blauiltQaAqw6yik .node .label,#mermaid-svg-blauiltQaAqw6yik .image-shape .label,#mermaid-svg-blauiltQaAqw6yik .icon-shape .label{text-align:center;}#mermaid-svg-blauiltQaAqw6yik .node.clickable{cursor:pointer;}#mermaid-svg-blauiltQaAqw6yik .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-blauiltQaAqw6yik .arrowheadPath{fill:#333333;}#mermaid-svg-blauiltQaAqw6yik .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-blauiltQaAqw6yik .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-blauiltQaAqw6yik .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-blauiltQaAqw6yik .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-blauiltQaAqw6yik .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-blauiltQaAqw6yik .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-blauiltQaAqw6yik .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-blauiltQaAqw6yik .cluster text{fill:#333;}#mermaid-svg-blauiltQaAqw6yik .cluster span{color:#333;}#mermaid-svg-blauiltQaAqw6yik div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-blauiltQaAqw6yik .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-blauiltQaAqw6yik rect.text{fill:none;stroke-width:0;}#mermaid-svg-blauiltQaAqw6yik .icon-shape,#mermaid-svg-blauiltQaAqw6yik .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-blauiltQaAqw6yik .icon-shape p,#mermaid-svg-blauiltQaAqw6yik .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-blauiltQaAqw6yik .icon-shape .label rect,#mermaid-svg-blauiltQaAqw6yik .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-blauiltQaAqw6yik .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-blauiltQaAqw6yik .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-blauiltQaAqw6yik :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-blauiltQaAqw6yik .cookie>*{fill:#ffebee!important;stroke:#c62828!important;stroke-width:2px!important;color:#b71c1c!important;font-weight:bold!important;}#mermaid-svg-blauiltQaAqw6yik .cookie span{fill:#ffebee!important;stroke:#c62828!important;stroke-width:2px!important;color:#b71c1c!important;font-weight:bold!important;}#mermaid-svg-blauiltQaAqw6yik .cookie tspan{fill:#b71c1c!important;}#mermaid-svg-blauiltQaAqw6yik .obj>*{fill:#e3f2fd!important;stroke:#1565c0!important;stroke-width:2px!important;color:#0d47a1!important;}#mermaid-svg-blauiltQaAqw6yik .obj span{fill:#e3f2fd!important;stroke:#1565c0!important;stroke-width:2px!important;color:#0d47a1!important;}#mermaid-svg-blauiltQaAqw6yik .obj tspan{fill:#0d47a1!important;}#mermaid-svg-blauiltQaAqw6yik .dots>*{fill:#f5f5f5!important;stroke:#9e9e9e!important;stroke-width:1px!important;color:#616161!important;}#mermaid-svg-blauiltQaAqw6yik .dots span{fill:#f5f5f5!important;stroke:#9e9e9e!important;stroke-width:1px!important;color:#616161!important;}#mermaid-svg-blauiltQaAqw6yik .dots tspan{fill:#616161!important;}#mermaid-svg-blauiltQaAqw6yik .single>*{fill:#e8f5e9!important;stroke:#2e7d32!important;stroke-width:2px!important;color:#1b5e20!important;}#mermaid-svg-blauiltQaAqw6yik .single span{fill:#e8f5e9!important;stroke:#2e7d32!important;stroke-width:2px!important;color:#1b5e20!important;}#mermaid-svg-blauiltQaAqw6yik .single tspan{fill:#1b5e20!important;} 内存堆区 (Heap)
operator new 分配的原始内存块 (总大小 = sizeof(T))
operator new\[\] 分配的原始内存块 (总大小 = 5*sizeof(T) + 8)
指向
指向
指向
指向
🧁 Cookie (8字节)

值 = 5
📦 T0

对象数据
📦 T1

对象数据
... ...
📦 T4

对象数据
📦 单个对象数据

(无 Cookie)
👨‍💻 用户指针

arr = raw + 8
⚙️ 底层 raw 指针
👨‍💻 用户指针

ptr = raw
⚙️ 底层 raw 指针


#mermaid-svg-NETUIUoPW1VT2vgz{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-NETUIUoPW1VT2vgz .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NETUIUoPW1VT2vgz .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NETUIUoPW1VT2vgz .error-icon{fill:#552222;}#mermaid-svg-NETUIUoPW1VT2vgz .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NETUIUoPW1VT2vgz .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NETUIUoPW1VT2vgz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NETUIUoPW1VT2vgz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NETUIUoPW1VT2vgz .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NETUIUoPW1VT2vgz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NETUIUoPW1VT2vgz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NETUIUoPW1VT2vgz .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NETUIUoPW1VT2vgz .marker.cross{stroke:#333333;}#mermaid-svg-NETUIUoPW1VT2vgz svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NETUIUoPW1VT2vgz p{margin:0;}#mermaid-svg-NETUIUoPW1VT2vgz .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-NETUIUoPW1VT2vgz .cluster-label text{fill:#333;}#mermaid-svg-NETUIUoPW1VT2vgz .cluster-label span{color:#333;}#mermaid-svg-NETUIUoPW1VT2vgz .cluster-label span p{background-color:transparent;}#mermaid-svg-NETUIUoPW1VT2vgz .label text,#mermaid-svg-NETUIUoPW1VT2vgz span{fill:#333;color:#333;}#mermaid-svg-NETUIUoPW1VT2vgz .node rect,#mermaid-svg-NETUIUoPW1VT2vgz .node circle,#mermaid-svg-NETUIUoPW1VT2vgz .node ellipse,#mermaid-svg-NETUIUoPW1VT2vgz .node polygon,#mermaid-svg-NETUIUoPW1VT2vgz .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NETUIUoPW1VT2vgz .rough-node .label text,#mermaid-svg-NETUIUoPW1VT2vgz .node .label text,#mermaid-svg-NETUIUoPW1VT2vgz .image-shape .label,#mermaid-svg-NETUIUoPW1VT2vgz .icon-shape .label{text-anchor:middle;}#mermaid-svg-NETUIUoPW1VT2vgz .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-NETUIUoPW1VT2vgz .rough-node .label,#mermaid-svg-NETUIUoPW1VT2vgz .node .label,#mermaid-svg-NETUIUoPW1VT2vgz .image-shape .label,#mermaid-svg-NETUIUoPW1VT2vgz .icon-shape .label{text-align:center;}#mermaid-svg-NETUIUoPW1VT2vgz .node.clickable{cursor:pointer;}#mermaid-svg-NETUIUoPW1VT2vgz .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-NETUIUoPW1VT2vgz .arrowheadPath{fill:#333333;}#mermaid-svg-NETUIUoPW1VT2vgz .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NETUIUoPW1VT2vgz .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NETUIUoPW1VT2vgz .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NETUIUoPW1VT2vgz .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-NETUIUoPW1VT2vgz .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NETUIUoPW1VT2vgz .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-NETUIUoPW1VT2vgz .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NETUIUoPW1VT2vgz .cluster text{fill:#333;}#mermaid-svg-NETUIUoPW1VT2vgz .cluster span{color:#333;}#mermaid-svg-NETUIUoPW1VT2vgz div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-NETUIUoPW1VT2vgz .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-NETUIUoPW1VT2vgz rect.text{fill:none;stroke-width:0;}#mermaid-svg-NETUIUoPW1VT2vgz .icon-shape,#mermaid-svg-NETUIUoPW1VT2vgz .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NETUIUoPW1VT2vgz .icon-shape p,#mermaid-svg-NETUIUoPW1VT2vgz .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-NETUIUoPW1VT2vgz .icon-shape .label rect,#mermaid-svg-NETUIUoPW1VT2vgz .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NETUIUoPW1VT2vgz .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-NETUIUoPW1VT2vgz .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-NETUIUoPW1VT2vgz :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 💥 错误路径 2 (new 配 delete\[\])
💥 错误路径 1 (new\[\] 配 delete)
🎉 正确路径 (完美回收)
✅ 正确: delete\[\] arr
❌ 灾难: delete arr
❌ 灾难: 对单对象用 delete\[\]
写下代码: A* arr = new A5;
分配内存: raw = operator new\[\](5 * sizeof(A) + 8)
写入 Cookie: *(long*)raw = 5
构造对象: 在 raw + 8 处循环构造 5 次
得到指针: arr = raw + 8
接下来你写什么?
delete\[\] 执行流程
delete 执行流程
对单对象用 delete\[\]
反向偏移 8 字节,读取 Cookie
得知 N = 5
逆序调用 5 次 ~A()
回退指针到 raw 释放内存
✅ 堆完好,内存完全释放
认为只有一个对象,不读 Cookie
只调用 1 次 ~A() (剩余4个泄漏)
直接把 arr(raw+8) 传给 free()
💥 报错: free(): invalid pointer

或堆损坏(Heap Corruption)
强行读 ptr 前 8 字节(其实是堆管理器的元数据)
读出垃圾值(比如 100000)
循环调用 100000 次析构函数
💥 报错: Segmentation fault (段错误)

  • 点击表格内对应链接跳转对应内容⬇️⬇️⬇️
作者主页 吃透C语言专栏 数据结构 Gitee仓库 C++ Linux 算法

相关推荐
宛 禾6 小时前
Qt 控件入门:QLabel 从拖拽到对象树,一篇讲透
c++·qt·学习
hetao17338376 小时前
联盟场-提高组-R7 & ZYZSC-S-Round 11
c++·算法
程序喵大人6 小时前
【C++入门】编译链接模型 - 07 链接器怎样把符号对上号
开发语言·c++·编译器·编译链接·链接器
局部有解6 小时前
Hot100 - 三数之和
数据结构·c++·算法
longlongzihan7 小时前
C++ 常用数据结构速查:vector、stack、queue、list、树、图、哈希表等声明与核心操作
数据结构·c++·list
别动我齐刘海7 小时前
实时控制学习2
linux·c++·人工智能·学习·算法
hetao17338378 小时前
校内场-提高组 ZYZSC-S-Round 2
c++·算法
hetao17338379 小时前
2026-10-14 hetao1733837 的刷题记录
c++·算法
浅念-9 小时前
动态规划:线性DP——多状态DP与子数组专题笔记
c++·笔记·算法·leetcode·职场和发展·动态规划·力扣