C++ | 动态内存管理 new、delete (用法、底层)详解

news2024/9/22 23:34:39

目录

简单回顾C语言动态内存管理

new、delete的用法

内置类型

new

delete

自定义类型

new、delete底层讲解(重要)

operator new 与 operator delete

定位 new

结语


简单回顾C语言动态内存管理

在C语言的学习阶段

我们接触到了三个能在堆上开辟空间的函数——malloc、calloc、realloc

其中,malloc是开辟空间但是不初始化,calloc是开辟空间并且会将空间内容都初始化为0,而realloc则主要用于扩容,如果传入的指针为空,则效果和malloc一致

realloc又分为原地扩容和异地扩容

原地扩容就是扩容后的空间直接在原空间上往后延申,但有一个前提,就是在后扩展这段空间里面不能存在数据阻挡,否则就会变成异地扩

异地扩容就是原空间后面有被使用的空间,导致无法直接原地扩容,就需要另外开辟一块完整空间,最后再将数据拷贝到新空间,原空间销毁

但是在C++就不一样了,C++中的new会更为简洁,各位看官且往下看

new、delete的用法

内置类型

new

我们在C++如果要使用C语言中的malloc、calloc、realloc也是可以的,因为C++兼容C语言

但是日常没有人会这么用,因为有更好的选择——new

我们在C++中如果想要动态开辟一个空间,可以选择直接new,如下:

int main()
{
	//动态开辟一块空间
	int* ptr = new int;
	return 0;
}

如果我们要连续开辟一段空间呢?比如开辟10个,如下:

int main()
{
	//动态开辟一块空间
	int* ptr1 = new int;

	//动态开辟一段空间
	int* ptr2 = new int[10];

	return 0;
}

那如果我想要开辟了空间之后进行初始化呢?也是可以的,我们先来看单单开辟了一个空间的情况:

int main()
{
	//动态开辟一块空间并初始化
	int* ptr1 = new int(5);

	return 0;
}

接着我们再来看一看一段空间的情况:

int main()
{
	//动态开辟一段空间并初始化
	int* ptr2 = new int[10] {1, 2, 3, 4, 5, 6};

	return 0;
}

如上,我们会看到我们开辟了一段空间,如果要初始化的话,后面就需要跟上花括号{},而如上代码我们只输入了6个数字,这也就代表着我们对应的初始化了前六个,而后面剩下的四个则会默认初始化为0

delete

同样的,C语言中清除空间有free,在C++中对应有delete,如下展示几个例子:

int main()
{
	//动态开辟一块空间
	int* ptr1 = new int;
	delete ptr1;

	//动态开辟一块空间并初始化
	int* ptr2 = new int(5);
	delete ptr2;

	//动态开辟一段空间
	int* ptr3 = new int[10];
	delete[] ptr3;

	//动态开辟一段空间并初始化
	int* ptr4 = new int[10] {1, 2, 3, 4, 5, 6};
	delete[] ptr4;

	return 0;
}

如上我们可以看到,C++中的delete一共有俩种用法——delete、delete[]

当只有单独一个空间的时候,就使用delete

当有一段空间的时候,就使用delete[]

当然这也比较好记,因为这也和new一一对应

如上都是针对内置类型的情况,但其实祖师爷搞出这块儿主要针对的是自定义类型

自定义类型

我们先来写一个类,在该类的构造函数和析构函数里面分别打印一些内容方便我们检查结果

struct A
{
	A()
	{
		cout << "A()" << " ";
	}

	~A()
	{
		cout << "~A()" << " " ;
	}
};

然后我们再使用我们的new:

int main()
{
	A* a1 = new A;
	delete a1;

	cout << endl << endl;

	A* a2 = new A[10];
	cout << endl;
	delete[] a2;
	return 0;
}

我们可以看到,当我们使用了new来开辟一块A类型的空间的时候,他会在开辟空间的同时,还会调用对应的构造函数

如果是开辟了一段空间比如10个的话,就会连续调用10次构造函数并开辟对应大小的空间

而我们的delete就相反,他会调用对应的析构,如果是连续的就连续调用多次析构并销毁相应空间

new、delete底层讲解(重要)

operator new 与 operator delete

operator new 和 operator delete对标的是malloc和free

也就是说,这个就是开辟空间的,如果我们去看源码的话,我们会发现,operator new 其实就是对malloc的一个封装,operator new 的底层就是malloc,operator delete同理

但是好好的为什么要对 malloc 封装呢?直接用 malloc 不好吗?

各位回想一下,我们每次在使用完malloc之后,都需要检查一下空间有没有开辟成功,如果没有开辟成功,我们还需要进行返回,而且每写一个就需要检查一下,写10个malloc就需要对应写10个检查,这样子写得确实有点恼火

而在C语言种检查错误用的是错误码,是errno

但是C++使用了一种给更好的方法——异常(这个知识太超纲了,后面会重点学,这里知道即可)

所以本质上,operator new 就是对 malloc 和异常进行了一个封装,这也就使得C++中的new变得异常简洁,因为我们不需要检查,直接使用new即可,new里面有operator new,operator new 有malloc 和异常,所以我们不需要检查

但是我们上面又看到,开辟一个自定义类型的时候,还会调用其构造函数

综上,我们可以 得出一个结果:

new = operator new + 构造函数

operator new = malloc + 异常(错误检查)

相对的,delete也是大差不差,但是operator delete可没有异常,祖师爷在这块设计成这样单纯是为了对称,所以:

delete = operator delete+ 析构函数

operator delete = free

同理,再来看看 new[],其本质就是多次调用operator new和构造,仅此而已,delete[] 同理

也就是在内部会多开四个字节用来记录连续开了几块空间,再根据这四个字节里面的内容,来决定到底要调用几次构造,几次析构,简图如以下(假设连续开辟10个空间):

定位 new

一般情况下,我们会使用new,而new会自动调用构造函数

但是我们会遇到一个情况,这个情况出现在内存池中:我们如果频繁地申请空间,会使效率变低,所以就有了内存池,要申请先申请一大块空间,需要申请就到内存池中申请,内存池空间不够了再到堆里面去

但是,我们如果要在内存池中申请空间的话,就不能使用new,因为new默认会调用堆上面的空间,所以我们只能使用operator new,但是我们这时候需要在类外面调用构造,可是析构能在类外面显式调用,但是构造不行,所以就有了定位new的产生

定位new的语法有点子奇怪:

new (place_address) type或者new (place_address) type(initializer-list)

举个例子:

A* ptr = (A*)operator new(sizeof(A));
new(ptr)A(10); //定位new

ptr->~A();
operator delete(ptr);

如上这是只申请一块空间的情况,但如果是operator new[]的话,就应该写一个for循环,循环的次数就是需要调用构造函数的次数

A* ptr = (A*)operator new(sizeof(A) * 10);
for(int i = 0; i < 10; i++)
    new(ptr+i)A(5); //定位new

for(int i = 0; i < 10; i++)
    (ptr+i)->~A();
operator delete[](ptr);

结语

这篇博客详细讲解了C++中的动态内存管理,如果觉得对你有帮助的话,希望可以多多支持!!❀

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

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

相关文章

vulhub靶场之wordpress关卡(保姆级教程)

一.打开wordpress关卡 1.选择简体中文 然后添加信息 点击安装wordpress 1.2 登陆后台 1.3 在后台修改模板 1.4 找一个php文件&#xff0c;在最开头加入一句话木马 点击更新 然后访问默认模板路径 下面的是默认的模板路径 /wp-content/themes/twentyfifteen/404.php 然后…

JAVA基础 - 数据库编程

目录 一. 简介 二. 数据持久化 三. 安装MySQL 四. JDBC API 五. 三个重要接口 一. 简介 数据库编程是指使用编程语言与数据库进行交互&#xff0c;以实现数据的存储、检索、更新、删除等操作。 在数据库编程中&#xff0c;常见的任务包括&#xff1a; 连接数据库&#x…

Spring统一返回类型中关于String的问题

文章目录 1. 问题铺垫2. 解决方法3. 问题分析4 解决方法解释 1. 问题铺垫 首先设置了以下代码统一处理返回类型 ControllerAdvice public class ResponseAdvice implements ResponseBodyAdvice {Overridepublic boolean supports(MethodParameter returnType, Class converte…

GuLi商城-新增商品-获取分类下所有分组以及属性

/*** 根据分类id查询出所有的分组以及这些组里面的属性* @param catelogId* @return*/ @Override public List<AttrGroupWithAttrsVo> getAttrGroupWithAttrsByCatelogId(Long catelogId) {//1、查询分组信息List<AttrGroupEntity> attrGroupEntities = this.list(…

SwiftUI 中掌握 ScrollView 的使用:滚动可见性

文章目录 前言视图修饰符应用场景可见性完整示例ContentViewVideoPlayerViewScrollViewVisibilityApp 总结 前言 我们的滚动 API 中又有一个重要的新增功能&#xff1a;滚动可见性。现在&#xff0c;你可以获取可见标识符列表&#xff0c;或者快速检查并监控 ScrollView 内视图…

一文彻底搞懂 Fine-tuning - 超参数(Hyperparameter)

最近这一两周看到不少互联网公司都已经开始秋招提前批了。不同以往的是&#xff0c;当前职场环境已不再是那个双向奔赴时代了。求职者在变多&#xff0c;HC 在变少&#xff0c;岗位要求还更高了。 最近&#xff0c;我们又陆续整理了很多大厂的面试题&#xff0c;帮助一些球友解…

【Unity】web gl inputFied 中文输入,同时支持TextMeshInputFied,支持全屏

同时支持TextMeshInputFied&#xff0c;支持全屏。 使用github包【WebGLInput】&#xff1a;https://github.com/kou-yeung/WebGLInput 需要资源的在这里也可以下载 https://download.csdn.net/download/weixin_46472622/89600795 用于unity web gl 中文输入&#xff0c;只需…

本地项目提交到Gitee

在项目目录 右键 git bash here 可以在黑屏输入命令 也可以在项目里面 命令都是一样的 要排除哪些 git add . 添加所有文件 git commit -m "Initial commit" 提交到本地 git remote add origin https://gitee.com/xxxx/xxxx.git 添加远程仓库 …

2-54 基于matlab的模糊自适应PID控制器

基于matlab的模糊自适应PID控制器&#xff0c;PID参数的整定必须考虑到在不同时刻三个参数的作用及相互之间的关系。在线实时模糊自整定PID算法的基础上&#xff0c;通过计算当前系统e和误差变化率ec&#xff0c;利用模糊规则进行模糊推理&#xff0c;查询模糊矩阵表进行参数调…

xss漏洞原理及利用【万字详解】

文章目录 url处XSS图片处XSS攻击svg-xss概念复现 pdf-xss概念复现 游览器翻译-xssflash-xss概念常见造成xss中的swf文件函数举例说明&#xff1a; cookie的获取概念代码审计复现 cookie的获取概念代码审计复现 页面信息获取概念条件复现 xss配合MSf钓鱼概念复现 XSS修复对危险字…

47 集合操作与运算

1 增加与删除集合元素 集合对象的 add() 方法可以增加新元素&#xff0c;如果该元素已存在则忽略该操作&#xff0c;不会抛出异常&#xff1b;update() 方法合并另外一个集合中的元素到当前集合中&#xff0c;并自动去除重复元素。 s {1, 2, 3} print(s) s.add(3) # 增加元…

C# 设计模式之装饰器模式

总目录 前言 装饰器模式的主要作用就是扩展一个类的功能&#xff0c;或给一个类添加多个变化的情况。学习面向对象的都知道&#xff0c;如果想单纯的给某个类增加一些功能&#xff0c;可以直接继承该类生成一个子类就可以。应对一些简单的业务场景继承也就够了&#xff0c;但是…

学习笔记-优化问题

目录 一、目前的问题 1、axios 2、跨域问题 3. 路由安全 二、解决问题 1. 跨域问题 2. 优化URL devServer 1. 配置 devServer 2. 修改请求路径 3. 重启 vue 4. 测试 5. pathRewrite 6. 重启 7. 测试 3. 优化 res.data 4. 判断状态码 5. 引入axios 1. 创建自…

【C++】2.C++入门(2)

文章目录 6.引用6.1 引用概念6.2 引用特性6.3 使用场景6.4 const引用&#xff08;常引用&#xff09;6.5 引用和指针的区别 7.inline7.1inline代码举例&#xff1a;7.2inline代码错误示范7.3实现一个ADD宏函数的常见问题&#xff1a; 8.nullptr 6.引用 6.1 引用概念 引用不是…

Yolov8在RK3588上进行自定义目标检测(三)

参考 Yolov8在RK3588上进行自定义目标检测(一) Yolov8在RK3588上进行自定义目标检测(二) best.onnx转yolov8.rknn onnx转rknn需要用到rknn-toolkit2&#xff0c;这个工具暂时不支持windows&#xff0c;所以我们移步linux&#xff0c;我用的是虚拟机创建的ubuntu20.4的系统&a…

JS+H5美观的带搜索的博客文章列表(可搜索多个参数)

实现 美观的界面&#xff08;电脑、手机端界面正常使用&#xff09;多参数搜索&#xff08;文章标题&#xff0c;文章简介&#xff0c;文章发布时间等&#xff09;文章链接跳转 效果图 手机端 电脑端 搜索实现 搜索功能实现解释 定义文章数据: 文章数据保存在一个 JavaScri…

评价指标--深度学习

目录 1分类任务1.1 二分类1.1.1 含义介绍1.1.2 指标 1.2多分类 2图像分割2.1 常用指标2.2 具体含义2.3 代码实现 1分类任务 1.1 二分类 混淆矩阵 1.1.1 含义介绍 TP&#xff1a;预测为真所以是Positive&#xff0c;预测结果和真实结果一致所以为TrueTN&#xff1a;预测为假…

【Python 逆向滑块】(实战六)逆向滑块,并实现用Python+Node.js 生成滑块、识别滑块、验证滑块、发送短信

逆向日期&#xff1a;2024.08.04 使用工具&#xff1a;Python&#xff0c;Node.js 本章知识&#xff1a;逆向【NECaptchaValidate】参数并成功发送短信 文章难度&#xff1a;中等&#xff08;没耐心的请离开&#xff09; 文章全程已做去敏处理&#xff01;&#xff01;&#xf…

【SpringBoot】 定时任务之任务执行和调度及使用指南

【SpringBoot】 定时任务之任务执行和调度及使用指南 Spring框架分别通过TaskExecutor和TaskScheduler接口为任务的异步执行和调度提供了抽象。Spring还提供了支持应用程序服务器环境中的线程池或CommonJ委托的那些接口的实现。最终&#xff0c;在公共接口后面使用这些实现&…

POE服务机器人-快速开始

快速开始 POE与服务机器人部署服务机器人与poe集成迭代你的机器人其他 POE与服务机器人 在本快速入门指南中&#xff0c;我们将使用 Python 构建一个机器人服务器&#xff0c;然后将其与 Poe 集成。一旦您创建了由您的服务器驱动的 Poe 机器人&#xff0c;任何 Poe 用户都可以…