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

目录

前言

 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.mfbz.cn/a/291753.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AI:110-基于深度学习的药物分子结构生成与预测

🚀点击这里跳转到本专栏,可查阅专栏顶置最新的指南宝典~ 🎉🎊🎉 你的技术旅程将在这里启航! 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带有在本地跑过的关键代码,详细讲解供…

Unity ab包如何加密

「ab包」全称为 AssetBundle &#xff0c;是Unity提供的一种资源存储压缩包。其中储存了游戏的资源&#xff0c;如图片、模型、纹理、音视频、代码等文件。 由于ab包具有灵活储存、支持热更、包体较小且便于管理等优势&#xff0c;已经成为了市面上主流的游戏资源压缩方式。 …

Jmeter(七) - 从入门到精通 - 建立数据库测试计划实战<MySQL数据库>(详解教程)

1.简介 在实际工作中&#xff0c;我们经常会听到数据库的性能和稳定性等等&#xff0c;这些有时候也需要测试工程师去评估和测试&#xff0c;上一篇文章宏哥主要介绍了jmeter连接和创建数据库测试计划的过程,宏哥在文中通过示例和代码非常详细地介绍给大家&#xff0c;希望对各…

【中小型企业网络实战案例 七】配置限速

相关学习文章&#xff1a; 【中小型企业网络实战案例 一】规划、需求和基本配置 【中小型企业网络实战案例 二】配置网络互连互通【中小型企业网络实战案例 三】配置DHCP动态分配地址 【中小型企业网络实战案例 四】配置OSPF动态路由协议【中小型企业网络实战案例 五】配置可…

51单片机(STC8)-- GPIO输入输出

文章目录 I/O口相关寄存器端口数据寄存器端口模式配置寄存器&#xff08;PxM0&#xff0c;PxM1&#xff09;端口上拉电阻控制寄存器(PxPU)关于I/O的注意事项 配置I/O口I/O设置demoI/O端口模式LED控制&#xff08;I/O输出&#xff09;按键检测&#xff08;I/O输入&#xff09; S…

【模拟量采集1.2】电阻信号采集

【模拟量采集1.2】电阻信号采集 1 怎么测&#xff1f;2 测输入电阻电压即转为测模拟电压值&#xff0c;这里需要考虑选用怎样的辅助电阻&#xff1f;3 实际电路分析3.1 在不考虑 VCC-5V 电压的纹波等情况时&#xff08;理想化此时输入的 VCC 就是稳定的 5V&#xff09;3.2 若考…

拖拽式工作流好用吗?有何特点?

大家都知道&#xff0c;随着行业的进步和发展&#xff0c;低代码技术平台也迎来了蓬勃发展期。很多企业喜欢使用低代码实现提质增效的办公效果&#xff0c;拖拽式工作流是其中一个功能&#xff0c;是助力企业实现流程化办公的得力助手。那么&#xff0c;拖拽式工作流好用吗&…

robots.txt

####什么是robots.txt? ​ robots.txt是一个协议,我们可以把它理解为一个网站的"管家",它会告诉搜索引擎哪些页面可以访问,哪些页面不能访问。也可以规定哪些搜索引擎可以访问我们的网站而哪些搜索引擎不能爬取我们网站的信息等等,是网站管理者指定的"君子协议…

FMQL BOOT.bin固化文件生成及固化流程记录

FMQL BOOT.bin固化文件生成及固化流程记录 一、概述 此篇记录上海复旦微JFMQL15T开发板 烧录固化文件BOOT.bin生成及固化操作流程。 以上一篇文章FQML_AXI_GPIO工程构建调试记录 中的工程为基础&#xff0c;做更改。 二、vivado工程配置 2.1新建工程 打开FQML_AXI_GPIO工程…

Unity | Shader基础知识番外(向量数学知识速成)

目录 一、向量定义 二、计算向量 三、向量的加法&#xff08;连续行走&#xff09; 四、向量的长度 五、单位向量 六、向量的点积 1 计算 2 作用 七、向量的叉乘 1 承上启下 2 叉乘结论 3 叉乘的计算&#xff08;这里看不懂就百度叉乘计算&#xff09; 八、欢迎收…

视频号小店全新赛道,新手如何入驻?

我是电商珠珠 视频号小店为视频号团队所研发。距今为止也才发展了一年时间&#xff0c;在23年下半年掀起了不小的浪花。 我做视频号小店也有一年时间了&#xff0c;在他刚开始三个月的时候&#xff0c;就开始带着团队一起做。到现在也拥有了自己的视频号小店运营团队&#xf…

数模学习day06-主成分分析

主成分分析(Principal Component Analysis,PCA)主成分分析是一种降维算法&#xff0c;它能将多个指标转换为少数几个主成分&#xff0c;这些主成分是原始变量的线性组合&#xff0c;且彼此之间互不相关&#xff0c;其能反映出原始数据的大部分信息。一般来说当研究的问题涉及到…

P5534 【XR-3】等差数列————C++、C

目录 【XR-3】等差数列题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 样例 #2样例输入 #2样例输出 #2 提示 解题思路Code运行结果 【XR-3】等差数列 题目描述 小 X 给了你一个等差数列的前两项以及项数&#xff0c;请你求出这个等差数列各项之和。 等差数列&#…

JavaWeb——后端之Mybatis

四、Mybatis 概念&#xff1a; Mybatis是一款持久层&#xff08;Dao层&#xff09;框架&#xff0c;用于简化JDBC&#xff08;Sun操作数据库的规范&#xff0c;较繁琐&#xff09;的开发 历史&#xff1a; Apache的一个开源项目iBatis&#xff0c;2010年由apache迁移到了goog…

Spring见解

1.Spring概述 1.1.Spring介绍 Spring是轻量级Java EE应用开源框架&#xff08;官网&#xff1a; http://spring.io/ &#xff09;&#xff0c;它由Rod Johnson创为了解决企业级编程开发的复杂性而创建 1.2.简化应用开发体现在哪些方面&#xff1f; IOC 解决传统Web开发中硬编…

【MATLAB】EEMD_LSTM神经网络时序预测算法

有意向获取代码&#xff0c;请转文末观看代码获取方式~也可转原文链接获取~ 1 基本定义 EEMD-LSTM神经网络时序预测算法是一种结合了扩展经验模态分解&#xff08;EEMD&#xff09;和长短期记忆神经网络&#xff08;LSTM&#xff09;的时间序列预测方法。 EEMD是一种改进的EM…

spring见解2基于注解的IOC配置

3.基于注解的IOC配置 学习基于注解的IOC配置&#xff0c;大家脑海里首先得有一个认知&#xff0c;即注解配置和xml配置要实现的功能都是一样的&#xff0c;都是要降低程序间的耦合。只是配置的形式不一样。 3.1.创建工程 3.1.1.pom.xml <?xml version"1.0" en…

《论文阅读》基于情绪-原因转换图的共情回复生成

《论文阅读》基于情绪-原因转换图的共情回复生成 前言摘要模型架构图构建回复概念预测回复生成前言 今天为大家带来的是《EMPATHETIC RESPONSE GENERATION VIA EMOTION CAUSE TRANSITION GRAPH》 出版: 时间:2023.2.23 类型:共情对话生成 关键词:图网络;共情回复;情绪…

C++ 实现对战AI五子棋

个人主页&#xff1a;日刷百题 系列专栏&#xff1a;〖C/C小游戏〗〖Linux〗〖数据结构〗 〖C语言〗 &#x1f30e;欢迎各位→点赞&#x1f44d;收藏⭐️留言&#x1f4dd; ​ ​ 前言&#xff1a; 为了能够快速上手一门语言&#xff0c;我们往往在学习了基本语法后&#x…