C++动态内存管理 解剖new/delete详细讲解(operator new,operator delete)

2024-04-17 22:04

本文主要是介绍C++动态内存管理 解剖new/delete详细讲解(operator new,operator delete),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述
讨厌抄我作业和不让我抄作业的人
讨厌插队和不让我插队的人
讨厌用我东西和不让我用东西的人
讨厌借我钱和不借给我钱的人
讨厌开车加塞和不让我加塞的人
讨厌内卷和打扰我内卷的人

一、C++中动态内存管理

1.new和delete操作内置类型

2.new和delete操作自定义类型

二、operator new与operator delete函数

三、operator new[]与operator delete[]函数

四、new和delete的实现原理

五、完结撒❀

–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀-正文开始-❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–

一、C++中动态内存管理

我们在C语言中所学习的动态内存函数:malloc,calloc,ralloc这些在C++中也可以继续进行使用,但是使用起来比较麻烦,并且一些地方留有不足,因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。

再强调一次:new和delete是两个操作符

1.new和delete操作内置类型

操作方式如下:

void Test()
{// 动态申请一个int类型的空间int* ptr1 = new int;// 动态申请一个int类型的空间并初始化为10int* ptr2 = new int(10);// 动态申请10个int类型的空间int* ptr3 = new int[10];//delete完成空间的销毁delete ptr1;delete ptr2;//销毁连续多个空间delete[] ptr3;
}

new和delete对动态内存开辟的操作就是这么朴实无华。

在这里插入图片描述这里注意一下初始化开辟的一个空间数据用(),开辟多个空间内存用[ ],初始化开辟的多个空间类型数据用{},操作如下:

int* ptr0 = new int[10] {1, 2, 3, 4, 5};
delete[] ptr0;

注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[],注意:匹配起来使用。

2.new和delete操作自定义类型

操作如下:

class A
{
public:A(int a = 0): _a(a){cout << "A():" << this << endl;}~A(){cout << "~A():" << this << endl;}
private:int _a;
};
int main()
{// new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间还会调用构造函数和析构函数A* p1 = (A*)malloc(sizeof(A));A* p2 = new A(1);free(p1);delete p2;// 内置类型是几乎是一样的int* p3 = (int*)malloc(sizeof(int)); // Cint* p4 = new int;free(p3);delete p4;A* p5 = (A*)malloc(sizeof(A) * 10);A* p6 = new A[10];free(p5);delete[] p6;return 0;
}

大家可以运行上面代码,研究观察new和delete与malloc和free的区别。

注意:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与free不会。

二、operator new与operator delete函数

new和delete是用户进行动态内存申请和释放的操作符operator new和operator delete是系统提供的全局函数new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。

我们针对代码转到反汇编就可以看到:

在这里插入图片描述
而对于operator new其底层实现是通过malloc函数实现的,operator delete其底层是通过free实现的

见下图:

在这里插入图片描述
在这里插入图片描述这里的_free_dbg表示的就是free,因为free函数是一个宏:

#define   free(p)       _free_dbg(p, _NORMAL_BLOCK)

不仅从汇编我们可以认识operator new函数和operator delete函数,我们还可以看一下其是如何实现的:

/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间
失败,尝试执行空               间不足应对措施,如果改应对措施用户设置了,则继续申请,否
则抛异常。
*/
void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{// try to allocate size bytesvoid* p;while ((p = malloc(size)) == 0)if (_callnewh(size) == 0){// report no memory// 如果申请内存失败了,这里会抛出bad_alloc 类型异常static const std::bad_alloc nomem;_RAISE(nomem);}return (p);
}/*
operator delete: 该函数最终是通过free来释放空间的
*/
void operator delete(void* pUserData)
{_CrtMemBlockHeader* pHead;RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));if (pUserData == NULL)return;_mlock(_HEAP_LOCK);  /* block other threads */__TRY/* get a pointer to memory block header */pHead = pHdr(pUserData);/* verify block type */_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));_free_dbg(pUserData, pHead->nBlockUse);__FINALLY_munlock(_HEAP_LOCK);  /* release other threads */__END_TRY_FINALLYreturn;
}/*
free的实现
*/
#define   free(p)               _free_dbg(p, _NORMAL_BLOCK)

上面operator new函数和operator delete函数的实现代码大家简单看一下,理解即可,看不懂没有关系

重点是通过上述两个全局函数的实现知道,operator new 实际也是通过malloc来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间的

三、operator new[]与operator delete[]函数

我们在开辟多个类型数据时用new T[N],其内部调用的是operator new[]函数,而operator new[]函数其实就是由operator new函数实现完成的,见下面汇编代码:

在这里插入图片描述

在这里插入图片描述
同理delete[]函数内部实现是由operator delete[]函数实现的,而operator delete[]函数内部实现也是由operator delete函数所实现的:

在这里插入图片描述
在这里插入图片描述

operator new[]函数内部实现逻辑由operator new函数实现,那么为什么还需要operator new[]函数呢?

在上面的学习中,我们强调过new和delete配对使用,而new[]要和delete[]进行配对使用,new[]和delete[]是在创建多个类型数据时进行使用,这里对于内置类型来说的话其实没多大意义(因为内置类型不需要调用构造函数和析构函数),主要在自定义类型上面

new和delete在创建和销毁自定义类型分别会自动调用构造函数和析构函数,所以我们在创建多组自定义类型数据时需要根据自定义类型的个数来确定一共需要调用几次构造函数和析构函数

class A
{
public:A(int a = 0):_a(a){cout << "A(int a = 0)" << this << endl;}~A(){cout << "~A()" << this << endl;}
private:int _a;
};int main()
{A* ptr1 = new A; //operator new + 1次构造A* ptr2 = new A[10];//operator new[] + 10次构造delete ptr1;//1次析构 + operator deletedelete[] ptr2;//10次析构 + operator deletereturn 0;
}

而调用operator new[]函数就实现了对类型数据个数的存储,我们可以看下代码:

class A
{
public:A(int a = 0):_a(a){cout << "A(int a = 0)" << this << endl;}~A(){cout << "~A()" << this << endl;}
private:int _a;
};int main()
{A* ptr1 = new A[3];delete[] ptr1;return 0;
}

按照正常计算的话,new开辟A[3]其大小应为12字节,但是我们转到汇编代码:

在这里插入图片描述
会发现一共是16个字节,这里多出来的4个字节就是用来存储总数据个数所用的。

同理,对于operator delete[]函数也一样,operator new[]函数记录了所创建的总数居的多少,也是为下面operator delete[]函数调用多少次析构函数做铺垫。

对于operator delete[]函数,最后也会在内存空间中会向前偏移4个字节的位置再进行free,将所存储数据个数的4个字节一同free掉。

四、new和delete的实现原理

1.内置类型

如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL

2.自定义类型

· new的原理

1. 调用operator new函数申请空间

2. 在申请的空间上执行构造函数,完成对象的构造

· delete的原理

1. 在空间上执行析构函数,完成对象中资源的清理工作

2. 调用operator delete函数释放对象的空间

· new T[N]的原理

1. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请

2. 在申请的空间上执行N次构造函数

· delete[N]的原理

1. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理

2. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释
放空间

通过上面的学习我们可以发现,对于操作符,其内部都是由一条一条指令进行实现的,所以我们使用操作符 ,在编译器编译时就会将操作符转换成每一条指令执行。

五、完结撒❀

如果以上内容对你有帮助不妨点赞支持一下,以后还会分享更多编程知识,我们一起进步。
最后我想讲的是,据说点赞的都能找到漂亮女朋友❤
在这里插入图片描述

这篇关于C++动态内存管理 解剖new/delete详细讲解(operator new,operator delete)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/912976

相关文章

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Python使用国内镜像加速pip安装的方法讲解

《Python使用国内镜像加速pip安装的方法讲解》在Python开发中,pip是一个非常重要的工具,用于安装和管理Python的第三方库,然而,在国内使用pip安装依赖时,往往会因为网络问题而导致速... 目录一、pip 工具简介1. 什么是 pip?2. 什么是 -i 参数?二、国内镜像源的选择三、如何

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

Spring AI集成DeepSeek的详细步骤

《SpringAI集成DeepSeek的详细步骤》DeepSeek作为一款卓越的国产AI模型,越来越多的公司考虑在自己的应用中集成,对于Java应用来说,我们可以借助SpringAI集成DeepSe... 目录DeepSeek 介绍Spring AI 是什么?1、环境准备2、构建项目2.1、pom依赖2.2

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没

Goland debug失效详细解决步骤(合集)

《Golanddebug失效详细解决步骤(合集)》今天用Goland开发时,打断点,以debug方式运行,发现程序并没有断住,程序跳过了断点,直接运行结束,网上搜寻了大量文章,最后得以解决,特此在这... 目录Bug:Goland debug失效详细解决步骤【合集】情况一:Go或Goland架构不对情况二:

SpringBoot中使用 ThreadLocal 进行多线程上下文管理及注意事项小结

《SpringBoot中使用ThreadLocal进行多线程上下文管理及注意事项小结》本文详细介绍了ThreadLocal的原理、使用场景和示例代码,并在SpringBoot中使用ThreadLo... 目录前言技术积累1.什么是 ThreadLocal2. ThreadLocal 的原理2.1 线程隔离2

Python itertools中accumulate函数用法及使用运用详细讲解

《Pythonitertools中accumulate函数用法及使用运用详细讲解》:本文主要介绍Python的itertools库中的accumulate函数,该函数可以计算累积和或通过指定函数... 目录1.1前言:1.2定义:1.3衍生用法:1.3Leetcode的实际运用:总结 1.1前言:本文将详