C++入门全集(5):内存管理

news2024/9/29 17:34:09

前言

一、内存区域划分

二、C++的内存管理方式

2.1 对内置类型

2.2 对自定义类型

三、new和delete的底层实现

四、new和delete的原理

五、定位new

六、malloc/free和new/delete

 

前言

在C++中,内存管理是不可避免的一门必修课。C++对内存的自由度使其获得了更高的性能,以及更高的难度。内存泄漏往往是每个C++学习者绕不开的错误, 而内存管理的水平高低也能看出一个编程者的能力。

在C语言中,我们学习了malloc、calloc、realloc和free,对C语言的内存管理也有了大致的接触。

本文中我们来学习C++中的内存管理


一、内存区域划分

C++中,程序的内存区域从低地址到高地址划分如下:

  • 代码段:存储可执行程序的代码和只读常量
  • 数据段:存储已初始化的全局变量和静态变量
  • 堆:用于程序运行时动态内存分配,从低地址向高地址增长
  • 栈:又叫堆栈,存储非静态局部变量/函数参数和返回值等,从高地址向低地址增长

例如: 

globalVar是全局变量,staticGlobalVar是静态全局变量,存储在数据段中;

staticVar是静态局部变量,存储在数据段中;

localVar、num1、char2、pChar3和ptr1都是局部变量,存储在栈中;

*char2是在栈帧中的空间,存储在栈中;*pChar3是常量字符串abcd的第一个字符,在代码段中;

malloc动态开辟出的空间存储在堆上。


二、C++的内存管理方式

2.1 对内置类型

C++兼容C语言,所以C语言的内存管理方式在C++中可以正常使用

相比C语言使用malloc和free等函数进行内存管理,C++提出了自己的内存管理方式:通过newdelete操作符进行动态内存管理

以上是C语言和C++动态申请一个类型大小的空间、多个类型大小的空间和内存释放的方式

 

实际上,面对内置类型,用malloc和new没有本质的区别,最大的区别在于:new可以初始化

在默认情况下,new和malloc一样不会对内置类型进行初始化,但是我们可以提出需求

注意区分初始化和申请多个元素,一个是圆括号一个是方括号

我们new一个数组时也可以进行初始化,例如:

C++不推荐使用malloc和free,我们最好使用new和delete,并且记得务必匹配使用。

对于内置类型,malloc和new区别不大。new是为了自定义类型而生的。

2.2 对自定义类型

我们在创建自定义类型对象的时候,需要调用析构函数,销毁时需要调用构造函数

而如果我们使用malloc和free的话,是不会调用这两个函数的

使用new来为自定义类型对象申请空间,编译器才会调用构造函数为对象初始化;用delete为自定义类型对象释放空间,才会调用析构函数。


三、new和delete的底层实现

new和delete并不是函数,而是用户进行动态内存申请和释放的操作符

但是其底层还是需要调用函数。

new在底层调用operator new这个函数来申请空间,delete在底层调用operator delete函数来释放空间。operator new和operator delete是系统提供的全局函数。

注意:这两个函数不是new和delete的重载函数!!!

虽然函数名中带operator,但并不是重载函数,具有很强的误导性。

我们来看看这两个函数的实现:

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);
}

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;
}

通过上面两个全局函数的实现,可以看出operator new实际上也是通过malloc来申请空间的,如果malloc申请空间成功就直接返回,如果失败则执行用户提供的应对措施,如果用户提供该措施则继续申请空间,否则抛出异常。

operator delete最终也是通过free来释放空间的。就像引用的底层也是用指针的方式实现的。

需要注意,构造函数和析构函数不是通过这两个函数来调用的。 


四、new和delete的原理

(1)new的原理

  • 调用operator new函数申请空间
  • 在申请的空间上执行构造函数,完成对象的构造

(2)delete的原理

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

(3)new T[N]的原理

  • 调用operator new[]函数,而operator new[]函数实际上又会调用operator new函数完成N个T类型对象的空间申请
  • 在申请的空间上执行N次构造函数

(4)delete[]的原理

  • 在空间上执行N次析构函数,完成N个对象的资源清理
  • 调用operator delete[]函数,而operator delete[]函数又会调用operator delete函数来释放空间


五、定位new

定位new表达式用于在已分配的原始内存空间调用构造函数初始化一个对象

大部分情况下,我们直接使用new来给对象分配空间

但是有时候需要进行性能优化,我们会直接从内存池中拿空间,使用malloc开空间

平时我们需要分配空间时从操作系统——堆上开空间,每次有需求就要开一次,但是内存池一次从堆上拿走一个内存块(大块空间),就不需要我们重复的去申请,减少了与堆的交互,提升了效率

如果是自定义的对象,对于malloc出来的空间,则需要使用定位new来显式的调用构造函数进行初始化

使用格式:

构造函数不需要传参时:new(指针)类名

构造函数需要传参时:new(指针)类名(参数)

例如:

或者:

销毁的方式:


六、malloc/free和new/delete

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

不同点在于:

  1. malloc和free是函数,new和delete是操作符
  2. malloc申请的空间不会初始化,new可以进行初始化
  3. malloc申请空间时需要自行计算要开的空间大小,new只需要空间类型和元素个数
  4. malloc的返回类型为void*,需要强制转换类型,new不需要
  5. malloc申请空间失败时返回空指针,需要判空,new失败时抛出异常,需要捕获
  6. 为自定义类型对象申请空间时,malloc和free不会调用构造函数和析构函数,而new和free会调用

如果文章有误,欢迎在评论区指出

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

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

相关文章

鸿蒙Harmony应用开发—ArkTS声明式开发(通用属性:点击回弹效果)

设置组件点击时回弹效果。 说明: 从API Version 10开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 clickEffect clickEffect(value: ClickEffect | null) 设置当前组件点击回弹效果。 系统能力: SystemCapabilit…

no declaration can be found for element ‘rabbit:connection-factory‘

spring-mvc 配置 rabbitmq 出现问题。 我的解决方案如下: 1 找到配置文件 spring-rabbitmq.xml 我的配置文件叫:spring-rabbitmq.xml,你们按照自己的查找。 2 定位如下URI 接着 Ctrl鼠标左键 3 确定spring-rabbit-x.x.xsd 按照步骤2 &…

回溯算法01-组合(Java)

1.组合 题目描述 给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 示例 1: 输入:n 4, k 2 输出: [[2,4],[3,4],[2,3],[1,2],[1,3],[1,4]]示例 2: 输入&#x…

巨控GRM561/562/563Y系列标准OPCUA客户端协议访问西门子1200PLC

驱动名称: 标准OPCUA客户端协议 描述: 本机作主机,读写其他外部设备数据 ◆寄存器类型: UA寄存器 地址范围: 可用数据类型:[BIT] [INT8] [INT16] [INT32] [INT64] [UINT8] [UINT16] [UINT32] [UINT64] [FLOAT32] [FLOAT64] ◆寄存器类型: 设备特殊寄存器 地址…

光影交织:汽车穿越隧道的视觉盛宴

在繁忙的城市中,隧道成为了连接两端的重要通道。而对于汽车来说,穿越隧道不仅是一次简单的空间转移,更是一场融合了视觉、技术与安全的独特体验。 当汽车缓缓驶入隧道,外界的光线逐渐减弱,隧道内部的光线开始发挥作用。…

Centos7 排查流量异常进程

首先可以根据漏洞捕获的相关信息的目的端口,查看下监听详情,如:netstat -lnp|grep 9015 执行命令输出如下所示内容: 最终可排查得知: 也可以通过以下方式进行详细排查: 1、基本知识 1.1、iftop介绍 一般…

java核心技术第十一章学习笔记

swing常用用户界面组件 Swing和模型-视图-控制器设计模式 模型:存储内容,必须实现改变内容和查找内容的方法,不可见 视图:显示内容 控制器:处理用户输入 一个模型可以有多个视图,每个视图可以显示全部…

稀碎从零算法笔记Day6-LeetCode:长度最小的子数组

前言:做JD的网安笔试题,结果查找子串(单词)这个操作不会。痛定思痛,决定学习滑动数组 题型:数组、双指针、滑动窗口 链接:209. 长度最小的子数组 - 力扣(LeetCode) 来…

使用awk和正则表达式过滤文本或字符串 - 详细指南和示例

当我们在 Linux 中运行某些命令来读取或编辑字符串或文件中的文本时,我们经常尝试将输出过滤到感兴趣的特定部分。这就是使用正则表达式派上用场的地方。 什么是正则表达式? 正则表达式可以定义为表示多个字符序列的字符串。关于正则表达式最重要的事情之…

网络编程的学习

思维导图 多路复用代码练习 select完成TCP并发服务器 #include<myhead.h> #define SER_IP "192.168.125.73" //服务器IP #define SER_PORT 8888 //服务器端口号int main(int argc, const char *argv[]) {//1、创建用于监听的套接字int sfd -1;s…

文件操作与IO(3) 文件内容的读写——数据流

目录 一、流的概念 二、字节流代码演示 1、InputStream read方法 第一个没有参数的版本&#xff1a; 第二个带有byte数组的版本&#xff1a; 第三个版本 搭配Scanner的使用 2、OutputStream write方法 第一个版本&#xff1a; 第二个写入整个数组版本&#xff1a; …

小米澎湃和华为原生鸿蒙,那个更有发展前景?

小米的澎湃系统暂时不了解&#xff0c;但华为的鸿蒙系统值得一说。 就目前鸿蒙而言&#xff1b;24年初鸿蒙星河版面向开发者开放申请。其底座全线自研&#xff0c;去掉了传统的 Linux 内核以及 AOSP 安卓开放源代码项目等代码&#xff0c;仅支持鸿蒙内核和鸿蒙系统的应用。星河…

【UE 材质 Niagara】爆炸效果

目录 效果 步骤 一、材质部分 二、Niagara部分 效果 步骤 一、材质部分 1. 创建一个材质&#xff0c;这里命名为“M_Burst” 打开“M_Burst”&#xff0c;设置混合模式为半透明&#xff0c;设置着色模型为无光照&#xff0c;勾选双面显示 在材质图表中首先创建扰动效果 其…

网络编程作业day4

广播模型&#xff1a; 发送端&#xff1a; #include <myhead.h> int main(int argc, const char *argv[]) {//创建套接字int sfdsocket(AF_INET,SOCK_DGRAM,0);if(sfd-1){perror("socket error");return -1;}//设置套接字允许广播属性int broadcast1;if(sets…

蓝桥杯备赛 day2 | 4. 付账问题 5. 数字三角形

付账问题&#xff0c;关键是要了解整型的范围&#xff0c;确定获取输入数据的变量类型 需要注意的是int的十进制范围-32768 ~ 32767&#xff0c;那么我们可以知道&#xff0c;人数n是可以用int来装的&#xff0c;需付款数S应该是long long&#xff0c;获取的每个人初始钱数也应…

自动驾驶预测与决策规划(nuplan数据集)

欢迎大家关注我的B站&#xff1a; 偷吃薯片的Zheng同学的个人空间-偷吃薯片的Zheng同学个人主页-哔哩哔哩视频 (bilibili.com) 目录 1.概述 2 数据采集 3.开环与闭环仿真 4.数据注释 5.场景 6.规划框架 6.1Train 6.2Simulation 6.3Metric 6.4Visualization 7.下载…

JasperStudio中TextField文本框组件渲染之后,出现行间距不一致的问题

目录 1.1、问题描述 1.2、解决方案 1.1、问题描述 最近在处理线上遇到的一个问题,是有关JasperReports报表相关的问题,问题背景大概是这样的:我们的项目中使用了JasperReports来渲染报表,其中使用到了Text Field文本框组件,但是问题是渲染出来的数据直接会出现一些间距…

JAVA如何利用接口实现多继承问题

hello&#xff0c;上文带大家学习了java中类的继承&#xff0c;我们可以创建一个父类&#xff0c;将类中的共性抽取出来&#xff0c;通过子类继承的方式来实现代码的复用。今天带大家学习不同类之间的另外几种关系&#xff0c;即多态抽象类和接口。 多态的概念 多态&#xff0c…

【Spring底层原理高级进阶】Spring Kafka:实时数据流处理,让业务风起云涌!️

&#x1f389;&#x1f389;欢迎光临&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;特别推荐给大家我的最新专栏《Spring 狂野之旅&#xff1a;从入门到入魔》 &#x1f680; 本…

Microsoft@ppt@快速掌握核心功能@常用功能培训

文章目录 refs动画动画的用途逐部分显示内容实现问答效果部分地修改页面内容动画效果 常用窗口对象选择窗口&#x1f47a;批量选择对象 如何为重叠的对象高效的命名重命名方式方案1方案2对象重命名原则重命名后如何使用tips 动画窗口&#x1f47a; 幻灯片管理幻灯片母版幻灯片母…