【C++】C++的内存管理

目录

 

内存分布

动态内存管理

C语言的动态内存管理

C++的动态内存管理

对内置类型操作

对自定义类型操作

new[]和delete[]

开空间的细节

探讨匹配问题

定位new表达式


 

内存分布

栈:存放非静态局部变量,函数参数,返回值等。栈是向下增长的。

堆:用于动态内存分配,堆是向上增长的。

数据段:存放全局数据,静态数据。

代码段:可执行代码,只读常量

内存映射段:是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可以使用系统接口创建共享内存,做进程间通信
示意图
db9b1641210f47ea8cc52a5e8351d06e.png
下面代码中的变量都在哪里呢
int a = 0;
static int b = 0;void Teat()
{int c = 0;static int d = 0;int num[10] = { 0 };char char2[] = "abcd";const char* pc = "abcd"; int* ptr = (int*)malloc(sizeof(int) * 4);free(ptr);
}

解析

9f4be9feeb5c4f5d9bd1f1deddd279be.png


动态内存管理

C语言的动态内存管理

void * malloc(size_t size) 
在堆上开辟空间
开辟失败返回NULL
返回值要检查是否为空
接受返回值需要强转
void * calloc(size_t num, size_t size)
再堆上开辟num个size大小的空间,并把该空间初始化为0
开辟失败返回NULL
返回值要检查是否为空
接受返回值需要强转
void * realloc(void* ptr, size_t size) // (动态内存空间, 新空间的大小)
调整原有空间
调整失败返回NULL
不会初始化空间
用新的指针接收

C++的动态内存管理

C++兼容C语言,C语言的动态内存管理方式依然在C++中能用。但在C++的大部分场景中,C语言的动态内存管理方式并不适用。

C++引入了两个关键字

new:在堆上开辟空间

delete:释放new开辟的空间

对内置类型操作

对于内置类型而言,new和delete与C语言的动态内存管理基本一致

开辟一个int类型的空间

int* ptr = new int;

开辟一个int类型的空间,并初始化为0

int* ptr = new int(0);

释放空间

delete ptr;

开辟10个int类型的空间,并初始化

int* ptr = new int[10]{1, 2, 3, 4, 5, 6};

后面会自动补0

c3547abe170a4288b12bd01b40d03d0b.png

释放该空间

delete[] ptr;

注意:new和delete匹配,new[]和delete[]匹配。如果不匹配会怎么样,对内置类型来说最多是警告。1dc5f1c396b247048e971a4ca081c41f.png

对自定义类型操作

引入new和delete就是为了应对自定义类型

new在堆上开辟空间时,还会调用构造函数

delete释放堆上的空间时,还会调用析构函数

详解构造函数:http://t.csdnimg.cn/TZLEB

小编会用下述代码带大家感受一下这两点

class Date //日期类
{
public:Date() //构造函数:_a(new int[7]{0}) //为_a开空间   初始化列表,_year(2024),_month(4),_day (24){cout << "构造函数" << endl;}~Date() //析构函数{delete[] _a;  //释放_a的空间cout << "析构函数" << endl;}private:int _year;  //年int _month; //月int _day;  //日int* _a;  //指针};
Date* ptr = new Date;           delete ptr;  

new示意图6d2bdc83b25e4aa896641579bc0a9be3.png

delete示意图

3885b790077e4fab9b55e58831116e5f.png

而C语言的malloc()不能调用构造free()不能调用析构。所以C语言的动态内存管理对自定义类型并不适用。

注意:对自定义类型来说,new和delete要匹配,new[]和delete[]要匹配,为什么要匹配小编后面会细讲

operator newoperator delete函数

operator newoperator delete 是系统的全局函数。

new操作符底层调用的是operator new ,delete操作符底层调用的是operator delete。

速览定义

3fb8eed3549c4b779808179484c0f817.png83a0cdf692fd4a098f7f9819de2163d0.pngoperator new 的底层实际上还是由malloc开的空间,可以说operator new是malloc的封装。

operator delete 的底层调用的是free,operator delete 是 free的封装。

直接让new操作符调用malloc不好吗,为什么要把malloc封一层 operator new呢?

如果malloc开辟空间失败,会返回NULL。我们会手动检查接收的指针是否为空,并给自己提示错误码。而operator new 开辟空间失败会抛异常。

抛异常提示的信息会比错误码丰富,当程序运行到异常的地方时,程序会跳转到相应异常的地方处理代码。(这里只是简单的提一下抛异常的好处,让大家理解为什么要对malloc进行封装)

下面是operator new的一部分源码

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

下面是 operator delete 的部分源码

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

new[]和delete[]

new[]底层会调用operator new[],operator new[]底层会调用N次operator new 来为N个对象在堆上开辟空间。然后在申请的空间上调用N次构造函数。

示意图

28836757d0804e3aaac48356ec2768ec.png

delete[]会先调用N次析构对要释放空间进行数据和资源的清理,然后会调用operator delete[] ,operator delete[]会再调用operator delete释放该空间

示意图

818b74f041d44a9f8673fa98dd1dc426.png

开空间的细节

new[]在开空间时会多开4个字节或8个字节多开的空间用于储存new[]开空间的个数

小编用如下代码调试,带大家感受一下

class TestClass //测试类
{
public:TestClass(int a = 0)  //构造函数: _a(a){cout << "TestClass():" << this << endl; //打印对象的地址}~TestClass() //析构函数{cout << "~TestClass():" << this << endl; }
private:int _a; //私有数据
};void Test() //测试函数
{TestClass* ptr = new TestClass[5]; //在堆上实例化5个对象delete[] ptr; //释放空间}int main()
{Test();  
}

new5个对象

f0a519d9205542fab91196906abb7c19.png释放空间

07c2c5aff81148fea1b65847899b475f.png

小编解释一下具体的过程

首先new[]会先为5个对象开辟空间,并额外开4字节或8字节的空间来记录对象的个数。然后调用5次构造函数为空间中的数据初始化。

delete[]会先调用5次析构清理空间的数据,delete[]最底层的free会在额外空间的地址往后释放空间。

开额外空间记录对象的个数是为了让编译器直到要调用几次析构函数,因为我们并不会给delete[]传对象的个数

至此,我们再来探讨一下delete和new与delete[]和new[]的匹配问题

探讨匹配问题

内置类型:在堆上开空间时不涉及调用构造函数和析构函数,也不涉及复杂的内存分配。如果不匹配,出问题的几率不大(小编不鼓励大家这么写代码)。

自定义类型:一定要匹配!!!我们把两种不匹配的情况都分析一下

new和delete[]

TestClass* ptr = new TestClass; //在堆上实例化5个对象  delete[] ptr; //释放空间

如果new和delete[]匹配会发生很有意思的情况

为什么会一直调用析构函数呢?因为new不会额外开空间,而delete[]会把对象的前4个或8个字节存的值当作析构函数调用的次数,但这个值是个随机值。

这是一个很严重的问题,一直被调用的析构函数不是只作用于一块空间,对象后面的空间都会被析构函数无差别的攻击。

这就像一个有着高机动性并且可以随意篡改内存的野指针。

这样写编译器不会强制报错,大家一定要注意

new[]和delete

Date* ptr = new Date[5];
delete ptr;  

首先,编译器会给出警告

49b71aa87df44150a0d3415e22fc3a03.png

代码也跑不动

75ecaf4987c4417ca451a18d2835a026.png

这里就会发生很明显的内存泄露。deleete[]额外开的空间不会被释放。因为delete只会调用一次析构,对象空间的数据和资源不会清理干净。new[]和delete匹配会强制报错。


定位new表达式

了解即可

作用:为了能显示的调用对象的构造函数

写法

new(指向对象的指针)构造函数名

new(ptr)TestClass;  

new(指向对象的指针)构造函数名type(初始化列表)

new(ptr)TestClass[1, 2, 3, 4, 5];

上述代码用到了隐式类型转换,可以参考一下小编的这篇文章http://t.csdnimg.cn/5yM0z


本篇的内容到此结束啦

 

 

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

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

相关文章

【数字电路与系统】【北京航空航天大学】实验:时序逻辑设计——三色灯开关(五)、小结

本次实验&#xff08;一&#xff09;见博客&#xff1a;【数字电路与系统】【北京航空航天大学】实验&#xff1a;时序逻辑设计——三色灯开关&#xff08;一&#xff09;、实验指导书 本次实验&#xff08;二&#xff09;见博客&#xff1a;【数字电路与系统】【北京航空航天…

大语言模型——涌现能力

在现有文献中,大语言模型的涌现能力被非形式化定义为在小型模型中不存在但在大模型中出现的能力”,具体是指当模型扩展到一定规模时,模型的特定任务性能突然出现显著跃升的趋势,远超过随机水平。类比而言,这种性能涌现模式与物理学中的相变现象有一定程度的相似,但是仍然…

如何在Android应用中安全地使用SQLite数据库,并通过SQLCipher进行加密保护

Android内置SQLite轻量级关系型数据库,可以在Android应用中存储、检索和管理结构化数据。SQLite是一个无服务器的、零配置的、事务性的SQL数据库引擎,非常适合用于移动设备和桌面应用程序中。 SQLite特点: 「轻量级」:SQLite不需要单独的服务器进程或操作系统级别的配置。…

Vim学习笔记01~04

第01章&#xff1a; 遁入空门&#xff0c;模式当道 1.什么是vim Vim是一个高效的文本编辑工具&#xff0c;并且可以在编程开发过程中发挥越来越重要的作用。 事实上&#xff0c;有不少编程高手使用他们来进行代码的开发&#xff0c;并且对此赞不绝口。 2.本系列目的 但是让…

微信小程序按钮点击时的样式hover-class=“hover“

小程序的button组件很好用&#xff0c;按钮点击的时候会显示点击状态&#xff0c;默认的就是颜色加深 但是我们改变了button的背景色之后&#xff0c;就看不出点击效果了&#xff0c;解决起来也很简单 关键代码就是小程序的 hover-class 属性&#xff0c;需要注意的是&#xff…

C 语言实例 - 计算 int, float, double 和 char 字节大小

使用 sizeof 操作符计算int, float, double 和 char四种变量字节大小。 sizeof 是 C 语言的一种单目操作符&#xff0c;如C语言的其他操作符、–等&#xff0c;它并不是函数。 sizeof 操作符以字节形式给出了其操作数的存储大小。 #include <stdio.h>int main() {int …

代码随想录算法训练营Day8 | ● 344.反转字符串● 541. 反转字符串II● 54.替换数字● 151.翻转字符串里的单词● 55.右旋转字符串

&#xff08;记得重学&#xff09; ● 344.反转字符串 题目&#xff1a;编写一个函数&#xff0c;其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。 不要给另外的数组分配额外的空间&#xff0c;你必须原地修改输入数组、使用 O(1) 的额外空间解决这一…

让Springboot JpaAuditing 支持ZonedDateTime

我们项目需要支持国际化&#xff0c;那么日期时间就需要有时区了&#xff0c;否则我们在今天早上9点干的事&#xff0c;人家美国人看到的是明天的时间。所以我们在Auditable中的创建时间和更新时间我们都定义为ZonedDateTime. 然而在保存的时候却抛出如下错误&#xff1a; Cau…

Qt [获取Dump] 使用WindowsAPI实现生成MiniDump文件

说明 客户现场的软件偶发崩溃是程序开发者&#xff0c;比较头疼的事情。如何更快速的定位到问题点和解决掉&#xff0c;是开发应该具备的基本能力。 Windows提供了一系列的API&#xff0c;可以记录软件崩溃前的堆栈信息。下面就实现一个生成Dump文件的程序实例。 主要代码 回…

计算机系列之输入输出、中断、总线、可靠性、操作系统、进程管理、同步互斥

9、输入输出-中断-总线-可靠性 1、输入输出技术、中断 1、内存与接口地址的编址方法&#xff08;了解概念即可&#xff09; 计算机系统中存在多种内存与接口地址的编址方法&#xff0c;常见的是下面两种&#xff1a;&#xff08;了解概念即可&#xff09; 1&#xff09;内存…

ai大模型应用开发

随着人工智能技术的飞速发展&#xff0c;AI大模型应用开发已成为一个日益重要的领域。本文将从专业角度深入探讨AI大模型的应用开发&#xff0c;并思考其未来的深度影响和逻辑性。 编辑搜图 请点击输入图片描述&#xff08;最多18字&#xff09; ​【一、AI大模型的定义与特点…

Practice Exam: Oracle Cloud Infrastructure Generative AI Professional

Practice Exam: Oracle Cloud Infrastructure Generative AI Professional 1. In the simplified workflow for managing and querying vector data, what is the role of indexing?2. In which scenario is soft prompting appropriate compared to other training styles?3…

ASP.NET Core 3 高级编程(第8版) 学习笔记 03

本篇介绍原书的第 18 章&#xff0c;为 19 章 Restful Service 编写基础代码。本章实现了如下内容&#xff1a; 1&#xff09;使用 Entity Framework Core 操作 Sql Server 数据库 2&#xff09;Entity Framework Core 数据库迁移和使用种子数据的方法 3&#xff09;使用中间件…

Babylon.js 读取GLB模型元数据

如果你熟悉将 3D 资源导出到游戏引擎的过程&#xff0c;那么无疑也会熟悉 3D 资源的 PBR 和 GLB 导出过程。 这是我们之前概述的内容&#xff0c;也是我们交互式工作的所有资产准备的基石。 然而&#xff0c;从传统的管道意义上来说&#xff0c;能够用元数据标记网格有很多逻辑…

通配符HTTPS安全证书

众多类型的SSL证书&#xff0c;要说适用或者说省钱肯定是通配符了&#xff0c;因为谁都想一本SSL证书包括了整条域名&#xff0c;而且也不用一条一条单独管理。 通配符HTTPS安全证书&#xff0c;其实就是通配符SSL证书&#xff0c;SSL证书主流CA的参数都一样&#xff0c;通配符…

【MHA】MySQL高可用MHA介绍8-常见错误以及解决

目录 MHA Node 未安装在 MySQL 服务器上 找不到主服务器的二进制日志 没有对二进制/中继日志授予读权限 使用多主复制&#xff08;不受支持&#xff09; 这些日志是 MHA Manager 在执行故障检测和故障切换过程中遇到的一些问题的记录。让我为您解释一下每个错误消息的含义&…

武汉星起航:亚马逊助力中国卖家扬帆出海,迎来跨境电商新机遇

2015年&#xff0c;亚马逊全球开店业务正式踏入中国这片充满活力和潜力的市场&#xff0c;此举不仅为中国卖家提供了前所未有的跨境电商新机遇&#xff0c;更为其发展出口业务、拓展全球市场、打造国际品牌铺设了一条坚实的道路。亚马逊作为国际版的电商购物平台&#xff0c;其…

Hadoop-Hive-Spark-离线环境搭建

一、版本描述 apache-hive-2.3.9-bin.tar.gz hadoop-2.7.0.tar.gz spark-2.4.0-bin-hadoop2.7.tgz 下载链接&#xff1a; https://archive.apache.org/dist/spark/spark-2.4.0/spark-2.4.0-bin-hadoop2.7.tgz https://archive.apache.org/dist/hadoop/common/hadoop-2.7.…

js中onchange的使用场景及如何使用

使用场景&#xff1a; onchange 事件常用于表单元素&#xff08;如 input、select、textarea 等&#xff09;的值发生改变时触发相应的操作。可以用于实时监测用户输入的内容&#xff0c;进行验证或实时更新相关内容。 示例代码&#xff1a; <!DOCTYPE html> <html…

vscode 创建代码模版

在vscode中快捷创建代码模版 1.在VSCode中&#xff0c;按下Ctrl Shift P&#xff08;Windows/Linux&#xff09;或Cmd Shift P&#xff08;Mac&#xff09;打开命令面板。 2.然后输入"Preferences: Configure User Snippets"并选择该选项。打开一个json文件用户…