【C++入门】C++内存管理

news2024/11/28 6:44:32

目录

前言

 C/C++内存分布

 C++内存管理方式

1. new和delete操作内置类型

快速了解与使用

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

3. operator new与operator delete

 4. operator new [ ] 

 *5.定位new

 6. malloc/free和new/delete的区别

总结


前言

         C++作为一种面向对象的编程语言,继承了C语言的内存管理特性,同时也引入了更加灵活和高级的内存管理机制。在C++中,内存管理涉及到动态内存分配、内存释放、内存泄漏等问题,对于程序的性能和稳定性都有着重要的影响。本文将详细介绍C++中的内存管理。

在这里插入图片描述

 C/C++内存分布

我们先来了解一下C/C++中内存区域的划分:

:栈是用来存放局部变量和函数调用信息的地方。当一个函数被调用时,它的参数和局部变量会被压入栈中,当函数执行完毕时,这些数据会被自动弹出。栈的大小是有限的,通常在几兆字节到几十兆字节之间

:堆是用来存放动态分配的内存的地方。在堆中分配的内存需要手动释放,否则会导致内存泄漏。堆的大小通常受系统总内存的限制,可以动态扩展

数据段:这个区域用来存放全局变量和静态变量。全局变量在程序整个运行周期内都存在,而静态变量在它们所在的函数执行期间存在,但是在程序整个运行周期内都存在

代码段:这个区域存放程序的代码和常量数据,如字符串常量。这部分内存通常是只读的

 目前的学习了解这几个即可。

 C++内存管理方式

1. new和delete操作内置类型

        C++继承了C语言的内存管理特性,C语言内存管理方式在C++中可以继续使用,但在C++一些使用场景下C的内存管理使用时又有些比较麻烦。于是C++引入了自己的内存管理方式:通过new和delete操作符进行动态内存管理。

快速了解与使用

 使用示例:

int main()
{
	// 动态申请一个int类型的空间
	int* p1 = new int;
	// 动态申请一个int类型的空间并初始化为10
	int* p2 = new int(10);
	// 动态申请10个int类型的空间
	int* p3 = new int[3];
	delete p1;
	delete p2;
	delete[] p3;
}
  • new和delete要配合使用

 不可malloc开空间,delete释放或者new开空间,free释放

  •  new和delete使用时类型要相符

比如:使用 new 开空间,delete [ ] 释放,new  [ ] 开空间,delete 释放 (不可)

  • new 和delete申请和释放空间必须是完整连续的

 与C语言中malloc和free类似,使用new来申请一块内存空间,那么必须使用delete来释放整块内存

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

 以一个简单的栈为例:

class Stack
{
public:
	Stack(int capacity = 4)
	{
		cout << "Stack(int capacity = 4)" << endl;

		_a = new int[capacity];
		_top = 0;
		_capacity = capacity;
	}

	~Stack()
	{
		cout << "~Stack()" << endl;

		delete[] _a;
		_a = nullptr;
		_top = 0;
		_capacity = 0;
	}
private:
	int* _a;
	int  _top;
	int  _capacity;
};



int main()
{
	Stack* p1 = new Stack;
	delete p1;
}

 new/delete对于  自定义类型  除了开空间还会调用构造函数和析构函数

Stack* p1 = new Stack; //主要干两件事:开空间 + 调构造

  1.  先给Stack 指针 p1开块空间
  2.  调用构造函数给p指向的空间进行初始化
delete p1; // 析构 + 释放空间

  1.  先释放_a的空间(先释放p1空间会导致_a指向空间丢失)
  2. 释放p1指向的空间

3. operator new与operator delete

 看到operator或许你已经有了猜想,new和delete其实就是操作符

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

Stack* p1 = new Stack;
delete p1;


Stack* p2 = (Stack*)operator new(sizeof(Stack));
operator delete(p2);

 在调用时new 和 operator new(delete 和 operator delete)其实是不一样的,通过两种方式的输出结果就可以看出来。

只有new 和 delete 调用了 构造 和 析构。

operator new 并不具备初始化的作用,它存在的意义:new对自定义类型是开空间 和 调构造,而operator new用于new的开空间操作

 operator delete 只有释放空间的作用,并不会调用析构函数 他的意义是为了与new 配对。

 4. operator new [ ] 

 开单个对象的空间我们了解了,那开多个对象的空间是怎么开的?

  1.  一次申请10个Stack对象的空间
  2.  调用10次构造函数(给每个对象的_a开空间)

 operator new [ ] 的底层其实调用的是operator new,实际调用的关系:

 operator new [ ] --> operator new --> malloc

operator new [ ]在函数内计算出要开的字节大小,然后使用operator new 去开空间:

 32位环境下,开10个stack对象的空间,size应该是120,可为什么是124?

这多出的4个字节开在开头位置:

 

 这多开出的4个字节空间存储的其实是new [ ]中的值(申请Stack对象的个数)。

        这个空间的数据就是申请空间Stack对象的个数,例子中我开10个Stack对象空间,所以存储的是10.

        这个10实际上是为delete [ ]  p3 准备的,在调用 delete [ ]时调用10次析构函数,释放每个对象_a的空间;

delete [ ] 释放空间时会连同这4个字节一起释放(让指针回到new [ ]所开空间最开始的位置)这也就是为什么建议使用 new 和 delete 时配合使用。

因为使用new [ ] 开空间,一旦自定义类型的对象涉及到内存申请,使用delete就会导致释放的空间不完整(delete会导致最开始的4字节空间不被释放),程序就会挂掉。

 建议: new / delete、new [ ] / delete [ ]、malloc / free 一定要配对使用

 *5.定位new

 在了解定位new之前,先思考一个问题,构造函数能不能显示调用?

 答案是不能,下边是测试的代码,可以自己测试一下。

class A
{
public:
	A(int a = 0)
		: _a(a)
	{
		cout << "A():" << this << endl;
	}
	~A()
	{
		cout << "~A():" << this << endl;
	}
private:
	int _a;
};

int main()
{
	A* p1 = (A*)operator new(sizeof(A));
	//p1->~A(1);
	return 0;
}

 虽然构造函数没法正常的进行显示调用,但是我们可以使用定位new来显示调用构造函数

new(p1)A; //new(p1)A(1);

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

使用格式:
new (place_address) type或者new (place_address) type(initializer-list)
place_address必须是一个指针,initializer-list是类型的初始化列

 构造函数不能直接显示调用,析构函数可以直接显示调用

p1->~A(); //调用析构
operator delete(p1); // 释放空间

 总的来说:

// new 效果
A* p1 = (A*)operator new(sizeof(A));
new(p1)A;

// delete 效果
p1->~A();
operator delete(p1); 

 应用场景:

那定位new有什么用?直接用 new 和 delete 不是更简洁

 定位new表达式在实际中一般是配合内存池使用

 在一些应用场景中,可能涉及到频繁的new申请空间,这样效率很低,为了解决效率问题,就有了池化技术——内存池

频繁的申请空间麻烦,那就一次多申请一些空间,使用时调用构造函数进行初始化

 这时调用构造函数就需要使用定位new。

 6. 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在释放空间前会调用析构函数完成空间中资源的清理


总结

         内存管理是C++编程中一个重要且复杂的主题,合理地使用new和delete可以避免内存泄漏和提高程序的性能。同时,了解定位new的使用场景也能让我们更好地控制内存分配和对象构造的过程。以上便是本文全部内容,希望对你有所帮助,感谢阅读!

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

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

相关文章

计算机丢失VCRUNTIME140_1.dll怎么办,6个不同方法教你解决问题

一、什么是vcruntime140_1.dll&#xff1f; vcruntime140_1.dll是Visual C Redistributable Packages的一部分&#xff0c;它是Microsoft Visual Studio开发环境中使用的运行时库文件。它包含了许多常用的函数和类&#xff0c;为开发者提供了丰富的功能支持。 二、vcruntime1…

Python 面向对象之封装和装饰器property

Python 面向对象之封装和装饰器property 【一】概念 封装是面向对象的三大特征之一封装&#xff1a;将属性和方法打包在一起&#xff0c;并对外部提供接口&#xff0c;控制外部对内部数据的访问和修改封装有助于隐藏对象的内部细节&#xff0c;提供更清晰的结构&#xff0c;提…

2023年终总结(脚踏实地,仰望星空)

回忆录 2023年&#xff0c;经历非常多的大事情&#xff0c;找工作、实习、研究生毕业、堂哥结婚、大姐买车、申博、读博、参加马拉松&#xff0c;有幸这一年全家人平平安安&#xff0c;在稳步前进。算是折腾的一年&#xff0c;杭州、赣州、武汉、澳门、珠海、遵义来回跑。完成…

安全狗入选“2023年福建省信息技术应用创新解决方案”名单

近日&#xff0c;福建省数字福建建设领导小组办公室公布了2023年福建省信息技术应用创新解决方案入选项目名单。 作为国内云原生安全领导厂商&#xff0c;安全狗凭借综合且具备突出创新水平的方案入选。 据悉&#xff0c;此次方案征集面向全省信创企业和用户单位&#xff0c;…

【Linux】——基本指令(二)

&#x1f497;个人主页&#x1f497; ⭐个人专栏——数据结构学习⭐ &#x1f4ab;点击关注&#x1f929;一起学习C语言&#x1f4af;&#x1f4ab; 目录 导读&#xff1a;1. vim 指令2. head指令3. tail指令4. tree指令5. 输出重定向6. echo指令7. wc指令8. | 字符9. date指令…

炼石免改造加密亮相2023商密大会,参编密评行业报告发布

2023年8月9-11日&#xff0c;2023商用密码大会在河南省郑州国际会展中心圆满召开&#xff0c;是我国商密领域规格最高、规模最大、影响最广的全国性商用密码盛会&#xff0c;也是《密码法》和新修订的《商用密码管理条例》正式实施以来的第一次全国性的盛会。大会以“密码赋能美…

51、全连接 - 特征的全局融合

Resnet50 中的核心算法,除了卷积、池化、bn、relu之外,在最后一层还有一个全连接。 下图是 Resnet50 网络结构结尾的部分,最后一层 Gemm(通用矩阵乘法)实现的就是全连接操作。而矩阵乘法我们之前介绍过,传送门在:矩阵乘。 卷积也好,矩阵乘法也好,其目的都是为了完成神…

Ajax基础入门_Ajax概述,同步与异步,Axios的使用,JSON数据及FastJSON的使用

Ajax 文章目录 Ajax1 概述2 作用3 同步和异步3.1 同步3.2 异步 4 代码编写4.1 服务端4.2 客户端 5 Axios5.1 使用5.2 代码5.2.1 前端5.2.2 后端 5.3 请求方法别名 6 JSON6.1 概述6.2 JSON 基础语法6.2.1 定义格式6.2.2 js 对象与JSON的转换 6.3 发送异步请求携带参数6.4 JSON串…

高分青海中心完成积石山6.2级地震(青海区域)卫星遥感数据与技术支撑工作

2023年12月18日23时59分&#xff0c;甘肃临夏州积石山县发生6.2级地震&#xff0c;青海省部分地区有明显震感&#xff0c;海东市民和县、化隆县、循化县出现不同程度人员伤亡和房屋受损情况。地震发生后&#xff0c;高分青海中心在国家航天局对地观测与数据中心的大力支持与紧急…

羊大师讲解,中国羊奶文化的丰富渊源

羊大师讲解&#xff0c;中国羊奶文化的丰富渊源 中国羊奶文化源远流长&#xff0c;几千年来与中华民族的生活息息相关。自古以来&#xff0c;中国人民就以羊为重要的家畜之一&#xff0c;不仅用于养殖和农业生产&#xff0c;更体现了中国人民的饮食文化和生活方式。中国羊奶文…

2分钟了解什么是socket?

文章目录 概念比喻类型Socket 与 TCP、UDP的关系 概念 Socket 是提供网络通信功能的编程接口&#xff08;API&#xff09;&#xff0c;提供了网络通信的基本操作&#xff0c;允许程序或进程之间进行数据交换。是传输层协议的具体软件实现&#xff0c;它封装了协议底层的复杂实…

mysql死锁排查

查看正在进行中的事务 SELECT * FROM information_schema.INNODB_TRX;字段解释trx_id唯一事务id号&#xff0c;只读事务和非锁事务是不会创建id的trx_state事务的执行状态&#xff0c;值一般分为&#xff1a;RUNNING, LOCK WAIT, ROLLING BACK, and COMMITTING.trx_started事务…

基于Java SSM框架实现中国古诗词学习平台项目【项目源码】

基于java的SSM框架实现中国古诗词学习平台系统演示 JSP技术介绍 JSP技术本身是一种脚本语言&#xff0c;但它的功能是十分强大的&#xff0c;因为它可以使用所有的JAVA类。当它与JavaBeans 类进行结合时&#xff0c;它可以使显示逻辑和内容分开&#xff0c;这就极大的方便了用…

LabVIEW开发分布式光纤油气管道泄漏检测及预警系统

LabVIEW开发分布式光纤油气管道泄漏检测及预警系统 随着油气工业的发展&#xff0c;管道泄漏成为一个严峻的安全问题。本文介绍了一种基于LabVIEW的分布式光纤油气管道泄漏检测及预警系统的设计思路和组成结构。系统包括硬件和软件两部分&#xff0c;其中硬件部分详细阐述了分…

进程的介绍及相关命令

首先&#xff0c;先了解一下计算机五大性能的命令 cpu top w 内存 top free 硬盘剩余 df 硬盘读写性能 iostat 网络带宽 iftop 一&#xff0c;进程与程序 1&#xff0c;什么是程序 &#xff1a; 硬盘上躺着&#xff0c;执行特点任务的一串代码 2&am…

AI边缘计算智能分析网关V4如何配置周界入侵检测算法

旭帆科技的智能分析网关V4内含近40种智能分析算法&#xff0c;包括人体、车辆、消防、环境卫生、异常检测等等&#xff0c;在消防安全、生产安全、行为检测等场景应用十分广泛&#xff0c;如常见的智慧工地、智慧校园、智慧景区、智慧城管等等&#xff0c;还支持抓拍、记录、告…

动态分区分配算法-第四十四天

目录 前言 首次适应算法&#xff08;First Fit&#xff09; 最佳适应算法&#xff08;Best Fit&#xff09; 最坏适应算法&#xff08;Worst Fit&#xff09; 临近适应算法&#xff08;Next Fit&#xff09; 本节思维导图 前言 动态分区分配算法&#xff1a;在动态分区分…

C#使用switch多路选择语句判断何为季节

目录 一、 switch语句 二、示例 三、生成 一、 switch语句 switch语句是多路选择语句&#xff0c;它通过一个表达式的值来使程序从多个分支中选取一个用于执行的分支。 switch表达式的值只可以是整型、字符串、枚举和布尔类型。 switch语句中多个case可以使用一个break。 在…

移动通信原理与关键技术学习(2)

1.多径信道滤波器表示&#xff0c;多径信道可以认为是线性时变滤波器&#xff0c;接收信号为发送信号与信道冲激响应的卷积。 2.调制就是对信号源的信息进行处理加到载波上&#xff0c;使其变为适合于信道传输的形式的过程&#xff0c;就是使载波随信号而改变的技术。 3.进行调…

CodeWave智能开发平台--03--目标:应用创建--01模板创建依赖问题修改

摘要 本文是网易数帆CodeWave智能开发平台系列的第03篇&#xff0c;主要介绍了基于CodeWave平台文档的新手入门进行学习&#xff0c;实现一个完整的应用&#xff0c;本文主要完成模板创建时的依赖问题解决。 CodeWave智能开发平台的03次接触 CodeWave参考资源 网易数帆Code…