C/C++内存管理(newdelete详解)

news2024/11/17 20:34:19

C/C++内存管理

  • 引言
  • new与delete的使用
    • 内置类型
    • 自定义类型
  • new与delete的实现原理
    • 内置类型
    • 了解operator new与operator delete函数
    • 自定义类型
  • 定位new
  • new&delete与malloc&free的区别
  • 总结

引言

在之前的C语言部分,我们已经介绍过内存中的存储区域的划分:包括内核空间(用户代码不能访问)、栈、内存映射段(文件映射,动态库,匿名映射)、堆、数据段(静态区)、代码段(常量区)
并且可以熟练的使用malloc、calloc、realloc、free来进行动态内存管理:
C语言动态内存管理详解

但是在C++中,使用这些函数来进行动态内存管理时,有一些需求是做不到的(比如申请空间时对对象初始化),而且使用时比较麻烦。所以C++提出了动态申请空间的方式newdelete

new与delete的使用

不同于malloc与free,new与delete是操作符而非函数

在使用new与delete申请空间时:
对于单个元素,用 new 类型; 即可申请一块指定类型的空间,表达式的值就是这块空间的指针。可以在类型的后面用括号初始化这个类型的值 new 类型(初始值);
对应的,需要用 delete 指针; 的形式来释放这块单个元素的空间。

对于连续的元素,用 new 类型[num]; 即可申请一块num个类型大小的连续空间,表达式的值就是这块空间的地址。可以在[num] 的后面用{}来初始化这块空间:new 类型[num]{};
对应的,需要用 delete[] 指针; 的形式来释放这块连续元素的空间。

内置类型

按照上面所介绍的规则,对于内置类型的动态申请空间有如下代码:

int main()
{

	int* p1 = new int;  
	//动态开辟一块int的空间,不初始化
	int* p2 = new int(10);  
	//动态开辟一块int的空间,初始化为10
	int* p3 = new int[10];  
	//动态开辟一块10个int的空间,不初始化
	int* p4 = new int[10]{ 1,2,3,4,5,6,7,8,9,0 }; 
	//动态开辟一块i10个int的空间,初始化为1,2,3,4,5,6,7,8,9,0

	delete p1;
	delete p2;
	//释放单个元素空间用delete
	delete[] p3;
	delete[] p4;
	//释放连续空间需使用delete[]

	return 0;
}

我们可以通过调试来查看各部分申请的空间中的值:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
需要注意的是,newdeletenew []delete[] 不能混用,在动态申请内置类型时虽然不会出现什么问题,但是对于自定义类型就会程序崩溃

自定义类型

对于内置类型,使用malloc与free还可以勉强达到我们的需求,但是对于自定义类型,比如类类型,我们在申请空间的时候,还需要初始化类对象,malloc显然不能满足我们的需求。

而new在动态申请空间之后,还会调用默认构造函数;delete在调用析构函数之后才会释放动态申请的空间。
这里需要特别注意类对象的空间与类对象中资源空间的区别

class A
{
public:
	A(int a = 0)
		:_a(a)
	{
		_date = new char[_a + 1] {0};
		cout << "A(int a = 0)" << endl; //构造函数中打印
	}
	~A()
	{
		delete[] _date;
		_a = 0;
		cout << "~A()" << endl;  //析构函数中打印
	}
private:
	int _a;
	char* _date;
};

例如这个A类,有两个成员变量intchar*,占8字节,这8个字节就是类对象的空间,这个空间可以在栈区,也可以动态申请在堆区,这取决于使用者;
但是其中char*指向一块连续的空间,这块空间的大小是不确定的,它是在类对象实例化的时候,在构造函数中动态申请的,这是使用类的人无法改变的。这块动态开辟的空间就是类的资源,它只能在构造函数中被申请,在析构函数中被释放。这也就是为什么malloc满足不了我们对与自定义类型动态申请空间的原因。

在这里插入图片描述

对于new自定义类型,用法上与内置类型一致:
在使用new申请单个对象时,调用一次构造函数,申请num个连续的对象时,就调用num次构造函数;delete同理:

int main()
{
	A* pa1 = new A;
	//动态开辟一个对象,调用默认构造初始化
	A* pa2 = new A(5);
	//动态开辟一个对象,传参给构造函数初始化
	A* pa3 = new A[5];
	//动态开辟一块连续的类对象空间,全部调用默认构造初始化
	A* pa4 = new A[5]{ 1,2,3,4,5 };
	//动态开辟一块连续的类对象空间,分别传参初始化

	delete pa1;
	delete pa2;
	//释放单个元素空间用delete
	delete[] pa3;
	delete[] pa4;
	//释放连续空间需使用delete[] !!!

	return 0;
}

我们可以通过调试来查看动态申请的情况:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
一共调用了1+1+5+5 =12次构造函数与12次析构函数:
在这里插入图片描述

new与delete的实现原理

内置类型

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

需要注意的是,在动态申请或释放空间时,对于申请或释放失败的情况,C++更倾向于抛异常来反映失败的原因。这个抛异常的行为是在operator newoperator delete两个函数中来实现的。
我们也可以通过反汇编来查看,在new一块空间时,确实调用了operator new函数,在delete时确实调用了operator delete函数:
在这里插入图片描述
在这里插入图片描述
当然,申请与释放连续的空间时,调用的是operator new[]operator delete[] 函数。而这两个函数中还是调用多个operator newoperator delete来实现的。

了解operator new与operator delete函数

我们可以来简单了解一下operator newoperator delete函数:

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

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

自定义类型

  1. new的原理
    调用operator new函数申请空间,然后在申请的空间上执行构造函数,完成对象的构造。

  2. delete 的原理
    在空间上执行析构函数,完成对象中资源的清理工作,然后调用operator delete函数释放对象的空间

  3. new T[num] 的原理
    调用operator new[]函数,在operator new[]中实际调用operator new函数完成num个对象空间的申请,在申请的空间上执行num次构造函数

  4. delete[] 的原理
    在释放的对象空间上执行num次析构函数,完成num个对象中资源的清理,然后调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

定位new

定位new可以用一块已经申请的动态空间调用构造函数来初始化类对象

定位new通常配合内存池来使用(内存池就是事先申请好的一块空间,事先申请一部分空间就可以避免因经常扩容而经常申请空间带来的效率的降低),内存池的空间只是申请,并没有初始化,我们就可以通过定位new来实现初始化:

使用定位new的格式为:new (要初始化的空间的指针) 类型;,需要注意的是,如果要初始化的类类型没有默认构造函数,就必须传参 new (要初始化的空间的指针) 类型(构造函数参数列表);
在释放定位new初始化的类对象的资源时,就需要显式的调用该类对象的析构函数来释放了:

class A
{
public:
	A(int a = 0)
		:_a(a)
	{
		_date = new char[_a + 1] {0};
		cout << "A(int a = 0)" << endl; //构造函数中打印
	}
	~A()
	{
		delete[] _date;
		_a = 0;
		cout << "~A()" << endl;  //析构函数中打印
	}
private:
	int _a;
	char* _date;
};

int main()
{
	A* ptr = (A*)malloc(1000);
	//创建1000个字节的空间
	new(ptr)A(5);
	//用上面申请的空间初始化一个A对象
	ptr->~A();
	//释放上面定位new初始化的对象的资源
	free(ptr);
	//释放malloc出的空间
	return 0;
}

需要注意的点还是类的空间与类的资源的空间的区别:这里malloc只是申请一块空间,下面的new是初始化malloc中的空间,这个初始化的行为包括申请类的资源,这个资源是不属于malloc出的空间的;
delete释放的是类的资源,而free掉的是malloc出的空间。

new&delete与malloc&free的区别

  1. malloc和free是函数;new和delete是操作符

  2. malloc申请的空间不会初始化;new可以初始化

  3. malloc申请空间时,需要手动计算空间大小并传递;new只需在其后跟上空间的类型即可,如果是多个对象,[num]中指定对象个数即可

  4. malloc的返回值为void*, 在使用时必须强转;new不需要,因为new后会指定空间的类型

  5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空;new不需要,但是new需要捕获异常

  6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数;而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理

总结

到此,关于C/C++内存管理的内容就介绍完了
相信大家不仅学会了C++中new与delete的使用,更对内存管理加深了理解

如果大家认为我对某一部分没有介绍清楚或者某一部分出了问题,欢迎大家在评论区提出

如果本文对你有帮助,希望一键三连哦

希望与大家共同进步哦

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

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

相关文章

FMQL在linux下GPIO的映射关系与使用

最近几年国产化芯片的使用如火如荼&#xff0c;部分国产手册说明杂且描述不清&#xff0c;尤其是一些映射关系使用文字描述非常不直观。博主在使用GPIO功能输出的时候反复看了几遍再去尝试控制耗费了不少时间。现结合相关文档总结GPIO映射表格&#xff0c;方便读者使用。 1、 …

【Spring Boot 】Spring Boot 统一功能处理

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔&#x1f93a;&#x1f93a;&#x1f93a; 目录 前言 1. Spring 拦截器 1.1 自定义拦截器 1…

6月19日第壹简报,星期一,农历五月初二

6月19日第壹简报&#xff0c;星期一&#xff0c;农历五月初二坚持阅读&#xff0c;静待花开1. 未来10天江南华南等地多降雨过程&#xff0c;国家防总将防汛四级响应范围扩至八省份&#xff0c;加派工作组赴地方协助指导。2. “一天有四季、十里不同天”&#xff0c;“最美”独库…

LVS - DR集群

LVS - DR集群 数据包流向分析&#xff1a; &#xff08;1&#xff09;客户端发送请求到 Director Server&#xff08;负载均衡器&#xff09;&#xff0c;请求的数据报文&#xff08;源 IP 是 CIP,目标 IP 是 VIP&#xff09;到达内核空间。 &#xff08;2&#xff09;Direct…

【VS2022】win 10 / win 11:Visual Studio 2022 社区版免费下载与安装

目录 一、Visual Studio 2022 下载 二、Visual Studio 2022 安装 三、Visual Studio 2022 快捷方式创建 四、Visual Studio 2022 使用 一、Visual Studio 2022 下载 Visual Studio 2022 官方下载https://visualstudio.microsoft.com/zh-hans/downloads/首先登陆 Visual Stu…

attention unet + cldice 论文总结

Blood Vessel Segmentation from Low-Contrast and Wide-Field Optical Microscopic Images of Cranial Window by Attention-Gate-Based Network论文总结 论文&#xff1a;Blood Vessel Segmentation by Attention-Gate-Based Network 目录 一、论文背景和出发点 二、创新点…

vue源码阅读之Observer

我们上次学习了vue数据驱动的概念&#xff0c;以及简单的vue怎么知道数据更新&#xff0c;然后采取行动的。今天我们就来继续深入学习&#xff0c;vue怎么把数据和视图给绑定在一起的&#xff0c;数据发生变化&#xff0c;视图怎么会自动发生变化的。 vue中的Observer 之前讲…

chatgpt赋能python:Python截取某一段文字

Python截取某一段文字 Python是最流行的编程语言之一&#xff0c;用于开发各种类型的应用程序&#xff0c;包括Web应用程序、桌面应用程序、游戏等。在本文中&#xff0c;我们将讨论如何使用Python截取某一段文字。这对于Web开发者和SEO专家非常有用&#xff0c;因为他们需要查…

【干货】Android系统定制基础篇:第八部分(增加以太网设置菜单、支持多摄像头、替换默认签名)

一、增加以太网设置菜单 Android 系统设置默认并没有以太网相关设置项&#xff0c;但以太网功能是支持的&#xff0c;因此我们仅仅需要增加设置界面即可。以太网设置界面如下&#xff1a; 修改 diff --git a/packages/apps/Settings/AndroidManifest.xml b/packages/apps/Se…

【第六次】21级计科计算机组成原理课外练习

【第六次】21级计科计算机组成原理课外练习 一、单选题二、多选题三、填空题 一、单选题 2-1 假定某计算机按字节编址&#xff0c;采用小端方式&#xff0c;有一个float型变量x的地址为0xffffc000&#xff0c;x12345678H&#xff0c;则在内存单元0xffffc001中存放的内容是 A.…

计算机网络和Linux网络

计算机网络和Linux网络 计算机网络概论 什么是计算机网络 计算机网络&#xff08;结构上&#xff09; 由节点&#xff08;主机、网络交换设备设备&#xff09;、边&#xff08;通信设备&#xff09;、协议构成协议&#xff1a;对等层的实体在通讯过程中应该遵守的规则的集合&…

软考A计划-网络工程师-易混淆知识汇总

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff…

chatgpt赋能python:Python编程:如何隐藏输入以保护信息安全?

Python编程&#xff1a;如何隐藏输入以保护信息安全&#xff1f; 随着数字化时代的到来&#xff0c;人们越来越多地依赖于互联网和技术设备进行通信和交易。然而&#xff0c;信息安全成为越来越大的问题&#xff1a;黑客和其他恶意分子通过各种手段获取和利用个人信息。为保障…

【瑞萨RA_FSP】GPT—— PWM功能详解

文章目录 一、GPT比较匹配功能详解1. 锯齿波PWM模式&#xff08;普通PWM模式&#xff09;2. 三角波PWM模式1&#xff08;波谷32位传输&#xff09;3. 三角波PWM模式2&#xff08;波峰和波谷32位传输&#xff09;4. 三角波PWM模式3&#xff08;波谷64位传输&#xff09;5. 设置死…

【随机种子初始化】一个神经网络模型初始化的大坑

1 问题起因和经过 半年前写了一个模型&#xff0c;取得了不错的效果&#xff08;简称项目文件1&#xff09;&#xff0c;于是整理了一番代码&#xff0c;保存为了一个新的项目&#xff08;简称项目文件2&#xff09;。半年后的今天&#xff0c;我重新训练这个整理过的模型&…

【C数据结构】带头双向循环链表_HDList

目录 带头双向循环链表_HDList 【1】链表概念 【2】链表分类 【3】带头双向循环链表 【3.1】带头双向循环链表数据结构与接口定义 【3.2】带头双向循环链表初始化 【3.3】带头双向循环链表开辟节点空间 【3.4】带头双向循环链表销毁 【3.5】带头双向循环链表头插 【3…

【C数据结构】带头单向非循环链表_HList

目录 带头单向非循环链表_HList 【1】链表概念 【2】链表分类 【3】有头单向非循环链表 【3.1】非循环链表数据结构与接口定义 【3.2】带头单向非循环链表初始化 【3.3】带头单向非循环链表释放空间 【3.4】带头单向非循环链表创建节点 【3.5】带头单向非循环链表头插…

HTML学习(二)

视频 <video width"320" height"240" controls> <source src"movie.mp4" type"video/mp4"> <source src"movie.ogg" type"video/ogg"> </video> 音频 <audio controls> <…

C++【AVL树】

✨个人主页&#xff1a; 北 海 &#x1f389;所属专栏&#xff1a; C修行之路 &#x1f383;操作环境&#xff1a; Visual Studio 2019 版本 16.11.17 文章目录 &#x1f307;前言&#x1f3d9;️正文1、认识AVL树1.1、AVL树的定义 2、AVL树的插入操作2.1、抽象图2.2、插入流程…

控制层调用接口的http请求封装

目录 0.碎碎念1.controller层2.util层3.测试3.1中间层调用GET请求3.2中间层调用POST请求 0.碎碎念 因为只是为了写这个帮助类&#xff0c;解耦&#xff0c;不敢拿已经写了一堆的代码改&#xff0c;就单独拆了个项目出来&#xff0c;持久层全是mybatisplus生成的。     所以…