C++笔记---内存管理

1. 内存分布

在对操作系统有更加深入的了解之前,在写代码的层面我们需要对下面的几个内存区域有所了解:

1. 栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的。

2. 堆--用于程序运行时动态内存分配,堆是可以上增长的。

3. 数据段--存储全局数据和静态数据。

4. 代码段--可执行的代码/只读常量。

 简单来说,直接定义的变量存放在栈中,动态申请的空间存放在堆中,全局或静态变量存放在数据段,字面量存放在代码段。

值得注意的是以下这段代码:

 const char* pChar = "abcd";int* ptr = (int*)malloc(sizeof(int) * 4);

这段代码中,“pChar”和“ptr”作为直接定义的变量,都是存放在栈中的。

而“abcd”作为只可读不可写的字面量存放在代码段,“pChar”中存放的是这个字符串的首元素地址。

malloc动态申请的空间存放在堆中,“ptr”只是存放其地址。

需要与上面的“pChar”形成区分的是:

char charArr[] = "abcd";

这段代码并非将“abcd”这一字面量字符串定义为了一个数组,而是以该字符串的内容为蓝本,在栈上定义了一个数组。

2. C++内存管理方式(new/delete)

在C语言中,我们使用malloc/calloc/realloc/free四个函数来进行内存管理。

C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因 此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。

申请/释放单个对象:

Type* pName = new Type(value);

delete pName;

// 动态申请一个int类型的空间
int* p1 = new int;// 动态申请一个int类型的空间,并将其初始化为10
int* p2 = new int(10);delete p1;
delete p2;

申请/释放数组:

Type* pName = new Type[num];

delete[] pName;

// 动态申请10个int类型的空间
int* p3 = new int[10];delete[] p3;

3. new/delete与malloc/free的区别

最主要的区别就是,new/delete在申请和释放自定义类型的对象时,会调用对应类的构造和析构函数,而malloc/free则不会。

究其本质,new/delete是按照类型来处理空间,malloc/free是按照大小来处理空间。

换句话来说,malloc并不知道自己申请的这块空间是用作何用的,准确来说是这块空间根本没有类型,只不过我们将malloc返回的指针进行了强转,并通过这个指针来对这块空间进行操作,使其具有了对应类型的性质。

由于空间本身不存在任何类型,自然在被申请和被释放时都不会有某个类的构造函数和析构函数参与。

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

 可以通过下面的方式来捕获异常:

try
{int* p = new int;
}
catch(const exception& e)
{cout << e.what() << endl;
}

 exception是C++的一种内置类型,what()函数会返回异常信息。

4. operator new与operator delete函数

new/delete在工作时,会经历“申请/释放空间”和“调用构造/析构函数”两步。

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

operator new函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,尝试执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。

void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{// try to allocate size bytesvoid *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函数实际也是通过free来释放空间的。

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

上面代码中的"_free_dbg"实际上就是free,头文件中free的实现如下:

 #define   free(p)               _free_dbg(p, _NORMAL_BLOCK)

用operator new和operator delete对malloc和free进行包装的目的是为了适应C++的异常抛出与捕获机制。

5.  new和delete的实现原理

new的原理:

1. 调用operator new函数申请空间;

2. 在申请的空间上执行构造函数,完成对象的构造。

delete的原理:

1. 在空间上执行析构函数,完成对象中资源的清理工作;

2. 调用operator delete函数释放对象的空间。

 new Type[num]的原理:

1. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请;

2. 在申请的空间上执行N次构造函数。

 delete[]的原理:

1. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理;

2. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释 放空间。

6. 定位new表达式(placement-new)

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

由于析构函数可以显式调用,所以不存在“定位delete表达式”。

使用格式:

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

place_address必须是一个指针,initializer-list是类型的初始化列表

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*)malloc(sizeof(A));new(p1)A(1);// 有默认构造时也可不要初始化列表p1->~A();free(p1);A* p2 = (A*)operator new(sizeof(A));new(p2)A(10);p2->~A();operator delete(p2);    return 0;
}

使用场景:

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

因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显式调构造函数进行初始化。

7. 申请释放方式不匹配

前面讲到,单个对象和申请数组有自己对应的申请释放方式。

自然,我们不建议混用,但是如果混用会出现什么问题呢?

由于new/delete的底层是malloc/free,所以内置类型进行混用不会出现问题,但自定义类型会。

1. new + free

析构函数不会被调用,如果对象中有动态资源的话,这些动态资源就没有被释放,会导致内存泄漏。

2. malloc + delete

构造函数不会被调用,对象不会进行初始化,未进行初始化的对象在调用成员函数时可能出现野指针或空引用等错误。

3. new [] + free

无析构函数时,正常;有析构函数时,程序会直接崩溃

new []还存在一个隐藏特性:如果类中有显式实现的析构函数,new []在开辟数组空间时,会在数组前额外开辟四个字节的空间,用于存储数组的元素个数,该数据存在的目的是告诉delete[]有多少个元素需要被释放。 

而返回的地址依然是数组首元素的地址。

由于这整个数组及数组前的空间是new利用malloc一并申请的连续的空间,所以当我们给free传入数组首元素的地址时,就会发生报错(free不能释放连续空间的某部分)。

4. malloc + delete[]以及new + delete[]

无析构函数时,正常;有析构函数时,由于缺少数组前的数据,会一直重复调用析构函数,具体机理不清楚(vs2022)。

 5. new [] + delete

delete单独释放首元素,导致与3相似的错误。

 由于是先调用析构函数再释放,所以此处调用了一次析构函数之后才发生报错。

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

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

相关文章

猫头虎分享:Python库 Httpx 的简介、安装、用法详解入门教程

猫头虎分享&#xff1a;Python库 Httpx 的简介、安装、用法详解入门教程&#x1f405; 大家好&#xff01;今天猫头虎来为大家分享一个在 Python 开发中非常实用的库——Httpx。 最近有很多粉丝问猫哥&#xff0c;Httpx 是什么&#xff1f;如何安装和使用&#xff1f;今天猫头…

深入解析SSRF和Redis未授权访问

深入解析SSRF和Redis未授权访问&#xff1a;漏洞分析与防御 在网络安全领域&#xff0c;服务器端请求伪造&#xff08;SSRF&#xff09; 和 Redis未授权访问 是两类常见且危险的安全漏洞。 1.2 SSRF攻击的利用 1.2.1 测试并确认SSRF漏洞 一个典型的例子是&#xff0c;当应用…

Java入门:06.Java中的方法--进阶04

4方法递归 简而言之就是方法的自身调用。 也可以是方法组自身的调用 递归类似循环&#xff0c;可以实现功能的反复执行。在某些(算法)环境下&#xff0c;比使用循环更轻松。 递归的本质就是方法的不同调用&#xff0c;就会不同的产生栈帧压栈&#xff0c;栈空间有限&#xff…

如何优雅的实现CRUD,包含微信小程序,API,HTML的表单(一)

前言 在开发实际项目中&#xff0c;其实CRUD的代码量并不小&#xff0c;最近要做一个小程序项目&#xff0c;由于涉及表单的东西比较多&#xff0c;就萌生了一个想法&#xff0c;小程序的写法不是和VUE类似&#xff0c;就是数据绑定&#xff0c;模块么&#xff01;那就来一个动…

redis核心数据结构源码分析

dictEntry和redisObject 在 Redis 的实现中&#xff0c;当一个键值对被创建并存储时&#xff0c;键通常是一个字符串&#xff0c;而值则是一个 redisObject。因此&#xff0c;在 dictEntry 结构中&#xff0c;key 成员指向的是一个字符串&#xff0c;而 v.val 成员则指向一个 …

IO进程day01(函数接口fopen、fclose、fgetc、fputc、fgets、fputs)

目录 函数接口 1》打开文件fopen 2》关闭文件fclose 3》文件读写操作 1> 每次读写一个字符&#xff1a;fgetc(),fputc() 针对文件读写 针对终端读写 练习&#xff1a;实现 cat 命令功能 格式&#xff1a;cat 文件名 2> 每次一个字符串的读写 fgets() 和 fputs() …

云原生系列 - Nginx(高级篇)

前言 学习视频&#xff1a;尚硅谷Nginx教程&#xff08;亿级流量nginx架构设计&#xff09;本内容仅用于个人学习笔记&#xff0c;如有侵扰&#xff0c;联系删学习文档&#xff1a; 云原生系列 - Nginx(基础篇)云原生系列 - Nginx(高级篇) 一、扩容 通过扩容提升整体吞吐量…

【非常简单】 猿人学web第一届 第12题 入门级js

这一题非常简单&#xff0c;只需要找到数据接口&#xff0c;请求参数 m生成的逻辑即可 查看数据接口 https://match.yuanrenxue.cn/api/match/12 查看请求对应的堆栈中的 requests 栈 list 为对应的请求参数 list 是由 btoa 函数传入 ‘yuanrenxue’ 对应的页码生成的 bto…

PD取电快充协议方案

PD快充协议是通过调整电压和电流来提供不同的充电功率。它采用了一种基于USB-C端口的通信协议&#xff0c;实现了充电器于设备之间的信息交换。在充电过程中设备会向充电器发出请求&#xff0c;要求提供不同的电压和电流&#xff0c;充电器接收到请求后&#xff0c;会根据设备的…

第6章 B+树索引

目录 6.1 没有索引的查找 6.1.1 在一个页中的查找 6.1.2 在很多页中查找 6.2 索引 6.2.1 一个简单的索引方案 6.2.2 InnoDB中的索引方案 6.2.2.1 聚簇索引 6.2.2.2 二级索引 6.2.2.3 联合索引 6.2.3 InnoDB的B树索引的注意事项 6.2.3.1 根页面万年不动窝 6.2.3.2 内节…

【vue】编辑器段落对应材料同步滚动交互

场景需求 编辑器段落对应显示材料编辑器滚动时&#xff0c;材料同步滚动编辑器段落无数据时&#xff0c;材料不显示 实现方法 编辑器与材料组件左右布局获取编辑器高度&#xff0c;材料高度与编辑器高度一致禁用材料组件的滚动事件获取编辑器段落距离顶部的位置&#xff0c;…

【机器学习-监督学习】支持向量机

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈Python机器学习 ⌋ ⌋ ⌋ 机器学习是一门人工智能的分支学科&#xff0c;通过算法和模型让计算机从数据中学习&#xff0c;进行模型训练和优化&#xff0c;做出预测、分类和决策支持。Python成为机器学习的首选语言&#xff0c;…

缓存学习

缓存基本概念 概念 对于缓存&#xff0c;最普遍的理解是能让打开某些页面速度更快的工具。从技术角度来看&#xff0c;其本质上是因为缓存是基于内存建立的&#xff0c;而内存的读写速度相比之于硬盘快了xx倍&#xff0c;因此用内存来代替硬盘作为读写的介质当然能大大提高访…

WIFI驱动开发

Linux 4.9 内核驱动移植 Linux 4.9 BSP 内核驱动 下载驱动后获得驱动的 tar.gz 压缩包 解压后找到如下驱动与文件夹 进入内核&#xff0c;找到 linux-4.9/drivers/net/wireless 文件夹中&#xff0c;新建文件夹aic8800 并且把上面的驱动与文件夹放入刚刚创建好的 aic8800 中。…

【笔记篇】Davinci Configurator SomeIpXf模块

目录 1 简介1.1 架构概览2 功能描述2.1 特性2.2 初始化2.3 状态机2.4 主函数2.5 故障处理3 集成4 API描述5 配置1 简介 本文主要描述了AUTOSAR SomeIpXf模块的功能。 SomeIpXf主要用途是对数据进行SOME/IP格式的序列化和反序列化。 1.1 架构概览 SomeIpXf在AUTOSAR软件架构…

【python】OpenCV—Single Human Pose Estimation

文章目录 1、Human Pose Estimation2、模型介绍3、基于图片的单人人体关键点检测4、基于视频的单人人体关键点检测5、左右校正6、关键点平滑7、涉及到的库函数scipy.signal.savgol_filter 8、参考 1、Human Pose Estimation Human Pose Estimation&#xff0c;即人体姿态估计&…

sqli-labsSQL手工注入第26-30关

第26关 一.查询数据库 http://127.0.0.1/Less-26/?id11%27%26extractvalue(1,concat(%27~%27,database(),%27~%27))%261%27 二.查表 http://127.0.0.1/Less-26/?id1%27||(updatexml(1,concat(1,(select(group_concat(table_name))from(infoorrmation_schema.tables)where(…

2月公开赛Web-ssrfme

考点&#xff1a; redis未授权访问 源码&#xff1a; <?php highlight_file(__file__); function curl($url){ $ch curl_init();curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_HEADER, 0);echo curl_exec($ch);curl_close($ch); }if(isset($_GET[url…

qt的model view 使用示范

首先在ui界面拖一个tableView ui->tableView->setModel(mission_model); 然后设置model的qss&#xff0c;并用view绑定model void SettingWidget::init_missionmodel(QString plane_type, QString mission_name) {if(mission_model)delete mission_model;mission_model…

论文导读 | 大语言模型中应用到的强化学习算法

摘要 在最近取得广泛关注的大规模语言模型&#xff08;LLM&#xff09;应用强化学习&#xff08;RL&#xff09;进行与人类行为的对齐&#xff0c;进而可以充分理解和回答人的指令&#xff0c;这一结果展现了强化学习在大规模NLP的丰富应用前景。本文介绍了LLM中应用到的RL技术…