【C++干货基地】深度理解C++中的高效内存管理方式 new delete

news2024/12/26 21:26:51

在这里插入图片描述

🎬 鸽芷咕:个人主页

 🔥 个人专栏: 《C++干货基地》《粉丝福利》

⛺️生活的理想,就是为了理想的生活!

引入

  哈喽各位铁汁们好啊,我是博主鸽芷咕《C++干货基地》是由我的襄阳家乡零食基地有感而发,不知道各位的城市有没有这种实惠又全面的零食基地呢?C++ 本身作为一门篇底层的一种语言,世面的免费课程大多都没有教明白。所以本篇专栏的内容全是干货让大家从底层了解C++,把更多的知识由抽象到简单通俗易懂。

⛳️ 推荐

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。

文章目录

  • 引入
  • ⛳️ 推荐
  • 一、C/C++内存分布
    • 1.1 内存布局图:
    • 1.2 C/C++程序内存分配的几个区域:
  • 二、C语言的内存管理方法
  • 三、C/C++ 中的内存管理方法
    • 3.1 new 和 delete 的使用
    • 3.2 new 和 delete 在创建自定义类型时候的动作
  • 四、new和delete的实现原理
    • 4.1 operator new与函数
    • 4.3 使用new 和new[ ] 是如何获取大小的
    • 4.4 delete 和 delete[ ] 的区别
  • 五、 malloc/free和new/delete的区别

一、C/C++内存分布

1.1 内存布局图:

在这里插入图片描述
用通俗易懂的话来描述就是:

  1. 栈区(stack):存放的是我们平常创建的变量 形参 等 临时变量!
  2. 堆区(heap):目前我们学的动态内存分配 都是在堆区开辟的!
  3. 数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放。
  4. 代码段可执行代码 和 只读 常量

1.2 C/C++程序内存分配的几个区域:

  1. 栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。
  2. 堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。分
    配方式类似于链表。
  3. 数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放。
  4. 代码段:存放函数体(类成员函数和全局函数)的二进制代码。

二、C语言的内存管理方法

在C 语言中 我们通常都是使用 malloc 来申请空间,使用 free 来释放空间

void Test()
{
	int* p1 = (int*)malloc(sizeof(int));
	free(p1);
	// 1.malloc/calloc/realloc的区别是什么?
	int* p2 = (int*)calloc(4, sizeof(int));
	int* p3 = (int*)realloc(p2, sizeof(int) * 10);
	
	free(p3);
}

但是malloc 还有 realloc 开辟空间都有失败的风向因此再项目中如果有开辟空间的行为是我们还得专门去写一个判断语句来避免空间开辟失败的其他报错。

void Test()
{

	int* tmp = (int*)malloc(sizeof(int));
	if (tmp == NULL)
	{
		perror("malloc file");
		exit(-1);
	}

	int* p1 = tmp;

	free(p1);
}

三、C/C++ 中的内存管理方法

C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。

  • 在使用C语言的内存管理方式是不能进行自己去创建类对象的
  • 而这也是我们设计 new 和 delete 的原因,更方便的开辟空间

3.1 new 和 delete 的使用

int main()
{
	// 1、用法上,变简洁了
	int* p0 = (int*)malloc(sizeof(int));
	int* p1 = new int;
	int* p2 = new int[10]; // new 10个int对象

	// 2、可以控制初始化
	int* p3 = new int(10); // new 1个int对象,初始化成10
	int* p4 = new int[10]{ 1,2,3,4,5 };
	
	return 0;
}

以上就是new 和 delete 的简单使用方法相信大家看一眼就会了非常的简单好上手。

3.2 new 和 delete 在创建自定义类型时候的动作


struct ListNode
{
	ListNode* _next;
	int _val;

	ListNode(int val)
		:_next(nullptr)
		, _val(val)
	{}
};

struct ListNode* CreateListNode(int val)
{
	struct ListNode* newnode = (struct ListNode*)malloc(sizeof(struct ListNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}

	newnode->_next = NULL;
	newnode->_val = val;
	return newnode;
}

以上是我们在C语言中开辟链表的方式,申请空间时书写非常麻烦还要去检查一下开辟空间是否失败。

  • 而new是可以直接去给我们生成空间和自动调用构造函数初始化的

struct ListNode
{
	ListNode* _next;
	int _val;

	ListNode(int val)
		:_next(nullptr)
		, _val(val)
	{}
};

int main()
{
	// 1、用法上,变简洁了
	int* p0 = (int*)malloc(sizeof(int));
	int* p1 = new int;
	int* p2 = new int[10]; // new 10个int对象

	// 2、可以控制初始化
	int* p3 = new int(10); // new 1个int对象,初始化成10
	int* p4 = new int[10]{ 1,2,3,4,5 };

	// 3、自定义类型,开空间+构造函数
	// 4、new失败了以后抛异常,不需要手动检查
	ListNode* node1 = new ListNode(1);
	ListNode* node2 = new ListNode(2);
	ListNode* node3 = new ListNode(3);
	//...
	return 0;
}

四、new和delete的实现原理

new 关键字的好用我们已经体验过,malloc 和 new相比简直一个天上一个地下,用过new的人都不会再选择malloc 了, 那他的底层究竟是怎么实现的呢?

  • 从下面这段代码我们可以看对内置类型进行new 开辟空间是去调用 operator new 函数来进行开辟空间的。
    在这里插入图片描述

4.1 operator new与函数

  • 那么operator new 函数是怎么实现的呢?
/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间
失败,尝试执行空               间不足应对措施,如果改应对措施用户设置了,则继续申请,否
则抛异常。
*/
void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
	// try to allocate size bytes
	void* 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_FINALLY
		return;
}
/*
free的实现
*/
#define   free(p)               _free_dbg(p, _NORMAL_BLOCK)

从这里我们就可以看到 operator new 实际上用 malloc 来封装实现开辟空间的

  • 也就是说,new 的底层是 调用 operator new 而 operator new 的底层是malloc
  • 那么new的底层实际上是对 malloc 封装实现的。

delete 也是同理在 delete 中我们发现 delete 是通过调用 operator delete 来实现开辟空间的而 operator delete 是通过 _free_dbg 来释放空间,_free_dbg 是free宏的 调用函数。

  • 所以 delete 的底层就是 对 free 进行封装实现的。

4.3 使用new 和new[ ] 是如何获取大小的

这个问题就很简单了,我们编译器其实是可以自动获取类型大小的,我们使用sizeof() 关键字都可以获取大小为什么编译器不可以呢?

  • 所以我们看到了,在汇编代码中一个 push 的大小就是我们要开空间的字节

在这里插入图片描述

  • 而 new[ ] 进行开辟连续的空间时我们就要注意了

本来我申请个连续的空间难道不是40个字节嘛,为什么给我多开辟了4个字节?

在这里插入图片描述

其实多出来的4个字节是为了在调用构造函数的时候记录需要构造的次数,已经析构的时候需要析构多少次。

  • new[10]进行开辟空间时的步骤是这样的
    在这里插入图片描述

4.4 delete 和 delete[ ] 的区别

前面我们看到了 在使用 new[ ] 进行开辟数组空间的时候其实会多开4个字节记录数组个数那么这个数组个数的作用是干嘛呢?

  • 记录数组的作用是为了给我们调用析构函数来用的
  • 大家看一下下面的这段代码,使用 new[ ] 创建的空间不使用 deletet[ ] 释放空间居然不报错。
class A
{
public:
	A(int a = 0)
		: _a(a)
	{
		cout << "A():" << this << endl;
	}


private:
	int _a;
};

int main()
{
	A* ptr1 = new A[10];
	delete ptr1;

	return 0;
}

在这里插入图片描述

这是因为我们一旦没有写析构函数的话,new[ ] 就不会多开4个字节。那么我们就行free的时候指针就是在开头的位置不要往前偏移才能释放

  • 而我们一旦写了析构函数new[ ] 就会为我们多开 4个字节存放数量

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

五、 malloc/free和new/delete的区别

malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。不同的地方是:

  1. malloc和free是函数,new和delete是操作符
  2. malloc申请的空间不会初始化,new可以初始化
  3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,如果是多个对象,[]中指定对象个数即可
  4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
  5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
  6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1622078.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【八股】Java基础、集合、JVM

面向对象三大特性 1 封装&#xff1a; 将 方法 和 属性 写到同一个类中&#xff0c;并将属性 私有化&#xff0c;生成 get set方法&#xff0c;外部访问属性需要通过get和set方法,内部可以直接访问属性&#xff0c;这样的一个类我们认为它完成了封装。 2 继承&#xff1a; 子…

【3GPP】【核心网】【LTE】S1MME流程字段分析(一)

1. 欢迎大家订阅和关注&#xff0c;精讲3GPP通信协议&#xff08;2G/3G/4G/5G/IMS&#xff09;知识点&#xff0c;专栏会持续更新中.....敬请期待&#xff01; 目录 1. Attach(23.401 5.3.2) 2. Service Request(23.401 5.3.4) 3. TAU (23.401 5.3.3) 3.1 Tracking Area Up…

《大话数据结构》12 图的相关算法

我有天早晨准备出门&#xff0c;发现钥匙不见了。昨晚还看到它&#xff0c;所以确定钥匙在家里。一定是我那三岁不到的儿子拿着玩&#xff0c;不知道丢到哪个犄角旮旯去了&#xff0c;问他也说不清楚。我现在必须得找到它&#xff0c;你们说&#xff0c;我应该如何找&#xff1…

华为OD机试 - 智能驾驶 - 广度优先搜索(Java 2024 C卷 200分)

华为OD机试 2024C卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测试…

「 网络安全常用术语解读 」什么是0day、1day、nday漏洞

1. 引言 漏洞攻击的时间窗口被称为漏洞窗口&#xff08;window of vulnerability&#xff09;。一般来说&#xff0c;漏洞窗口持续的时间越长&#xff0c;攻击者可以利用漏洞进行攻击的可能性就越大。 2. 0day 漏洞 0day 漏洞&#xff0c;又被称为"零日漏洞"&…

YUNBEE云贝-Oracle 19c OCM 5月19日

Oracle 19c OCM认证大师培训 - 课程体系 - 云贝教育 (yunbee.net) 19c OCM考试类别&#xff1f; Oracle 19c OCM认证大师直考(2天考试&#xff0c;4个模块&#xff0c;每个模块3小时) Oracle 19c OCM认证大师升级考(1天考试&#xff0c;2个模块&#xff0c;每个模块3小时) 在…

Day1--什么是网络安全?网络安全常用术语

目录 1. 什么是网络安全&#xff1f; 信息系统&#xff08;Information System&#xff09; 信息系统安全三要素&#xff08;CIA&#xff09; 网络空间安全管理流程 网络安全管理 2. 网络安全的常用术语 3. 网络安全形势 4. 中国网络安全产业现状 1. 什么是网络安全&am…

pnpm install报错 Value of “this“ must be of type URLSearchParams

执行pnpm install的时候就报错Value of “this” must be of type URLSearchParams 由于之前执行没有出现过这个问题&#xff0c;最近在使用vue3所以使用了高版本的node&#xff0c;怀疑是node版本的问题。 解决&#xff1a; 检查node版本 node -v当前使用的是20.11.0的 修改…

婚恋app系统开发相亲交友小程序源码单身交友app软件下载客户管理系统婚恋交友平台管理系统源码h5聊天室源码

在数字化时代&#xff0c;婚恋相亲交友小程序以其便捷、高效、智能的特点&#xff0c;受到了越来越多人的青睐。红娘婚恋相亲交友系统源码&#xff0c;凭借其对微信小程序、微信公众号、H5以及APP的全方位支持&#xff0c;成为了婚恋市场的明星产品。 首先&#xff0c;婚恋相亲…

力扣HOT100 - 124. 二叉树中的最大路径和

解题思路&#xff1a; class Solution {int max Integer.MIN_VALUE;public int maxPathSum(TreeNode root) {maxGain(root);return max;}public int maxGain(TreeNode node) {if (node null) return 0;int leftGain Math.max(maxGain(node.left), 0);int rightGain Math.ma…

go+react实现远程vCenter虚拟机管理终端

文章目录 React-VcenterDemoQuick Start React-Vcenter 基于go & react实现远程vSphere vcenter虚拟机终端console页面&#xff0c;提供与vcenter管理中的Launch Web Console相同的功能。 项目地址&#xff1a;react-vcenter Demo URL: http://localhost:3000 Quick St…

Unity 踩坑记录 Rigidbody 刚体重力失效

playerSetting > physics > Gravity > 设置 Y 的值为负数

《苍穹外卖》Day07部分知识点记录

一、菜品缓存 减少查询数据库的次数&#xff0c;优化性能 客户端&#xff1a; package com.sky.controller.user;import com.sky.constant.StatusConstant; import com.sky.entity.Dish; import com.sky.result.Result; import com.sky.service.DishService; import com.sky…

使用微软Phi-3-mini模型快速创建生成式AI应用

微软Phi-3大语言模型是微软研究院推出的新一代系列先进的小语言模型。Phi-3系列包括phi-3-mini、phi-3-small和phi-3-medium三个不同规模的版本。这些模型在保持较小的参数规模的同时&#xff0c;通过精心设计的训练数据集和优化的算法&#xff0c;实现了与大型模型相媲美的语言…

软件测试之【软件测试概论三】

读者大大们好呀&#xff01;&#xff01;!☀️☀️☀️ &#x1f525; 欢迎来到我的博客 &#x1f440;期待大大的关注哦❗️❗️❗️ &#x1f680;欢迎收看我的主页文章➡️寻至善的主页 文章目录 前言测试用例的前因后果测试用例的设计方法黑盒测试用例设计方法&#x1f525…

JavaEE 初阶篇-深入了解 I/O 流(FileInputStream 与 FileOutputStream 、Reader 与 Writer)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 I/O 流概述 2.0 文件字节输入流(FileInputStream) 2.1 创建 FileInputStream 对象 2.2 读取数据 2.3 关闭流 3.0 文件字节输出流(FileOutputStream) 3.1 创建 Fi…

html、css、QQ音乐移动端静态页面,资源免费分享,可作为参考,提供InsCode在线运行演示

CSDN将我上传的免费资源私自变成VIP专享资源&#xff0c;且作为作者的我不可修改为免费资源&#xff0c;不可删除&#xff0c;寻找客服无果&#xff0c;很愤怒&#xff0c;&#xff08;我发布免费资源就是希望大家能免费一起用、一起学习&#xff09;&#xff0c;接下来继续寻找…

智慧医院解决方案

5G智慧导航解决方案https://www.cgltzk.vip/doc/1537/ 深圳市妇幼保健院妇产科住院大楼智能化系统招标文件(技术规格书)https://www.cgltzk.vip/doc/1534/ 三甲医院智能化系统设计方案https://www.cgltzk.vip/doc/1423/ 医院智能化设计方案https://www.cgltzk.vip/doc/1422/ …

矩阵按列相乘运算的并行化实现方法

这两天一直在琢磨如下矩阵计算问题。 已知dm矩阵X和hq矩阵Y&#xff0c;求如下矩阵&#xff1a; 其中X(:,i), Y(:,j)分别表示矩阵X, Y的第i列和第j列&#xff0c;易知Z为dh矩阵。 如果直接串行计算矩阵Z&#xff0c;两个循环共有mq&#xff0c;则会很慢&#xff0c;能不能并行化…

UE5 GAS开发P34 游戏效果理论

GameplayEffects Attributes&#xff08;属性&#xff09;和Gameplay Tags&#xff08;游戏标签&#xff09;分别代表游戏中实体的特性和标识。 Attributes&#xff08;属性&#xff09;&#xff1a;Attributes是用来表示游戏中实体的特性或属性的值&#xff0c;例如生命值、…