CC++——深入探究动态内存管理

news/2024/11/8 12:16:07/

文章目录

  • 总述
  • C&C++程序内存区域划分
  • C++内存管理方式
    • 运用new/delete操作内置类型
    • new和delete操作自定义类型
  • operator new与operator delete函数
  • new和delete==操作符==的实现原理
    • 自定义类型
  • malloc/free 和 new/delete 的区别

总述

俗话说,没有理解过底层的c&c++程序员一定不是个优秀的程序员(doge),因此,在本篇博客中,将会大体地讲述一些c&c++程序内存管理的部分知识,并且再浅谈一下c++中是新增的操作(new,delete)如何进行动态内存管理的。

C&C++程序内存区域划分

首先我们需要了解一下C/C++程序中的数据是如何进行分布的,看下面这张图:
在这里插入图片描述

  1. 又叫做堆栈,用于存放非静态局部变量/函数参数/返回值等等,栈是向下生长的(朝内存地址减小的方向生长)
  • 在c/c++内存分配中,对于栈来说其数据是通过编译器自动进行管理的,无需我们手动控制,编译器会自动释放内存
  • 栈的空间很小,一般只有几MB的大小。
  1. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享内存,做进程间的通信。
  2. 用于程序运行时的动态内存分配,堆是向上生长的(朝内存地址增大的方向增长)

区别于栈,堆中的内存管理需要程序员控制,因此需要避免内存泄漏的问题。

  1. 数据段(静态区): 存储全局数据和静态数据
  2. 代码段(常量区): 存储可执行的代码/只读常量

C++内存管理方式

在C语言中,我们进行动态内存管理是用<stdlib.h>库文件中的malloc/calloc/realloc/free进行的,这些方式在C++中当然可以继续使用,但是有些地方就显得无能为力了,而且使用起来比较麻烦,因此C++也制作了一套自己的内存管理方式: 通过new和delete操作符进行动态内存管理

运用new/delete操作内置类型

看下面代码:

void Test()
{//动态申请一个int类型空间int* ptr = new int;//动态申请一个int类型的空间并将其初始化为10int* ptr2 = new int(10);//动态申请10个int类型的空间int* ptr3 = new int[10];//释放空间delete ptr;delete ptr2;delete ptr3;
}

是的,使用方法就是 new 类型名,在类型名之后括号内的内容表示对其进行初始化,而[]表示初始化对象的个数。

**注意:**申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[],切记一定要匹配起来使用!!!

new和delete操作自定义类型

对于自定义类型,new/delete和malloc/free差别确实不大,但是对于自定义类型来说,可以说new/delete简直是为自定义类型量身定做的!

看下面的代码:

#include <iostream>
#include <cstdlib>
using namespace std;
class A
{
private:int _a;int* _parr;
public:A(int a = 0):_a(a),_parr(new int[10]){cout << "A(int a = 0)" << endl;}~A(){delete[] _parr;cout << "~A()" << endl;}void set(){_parr = new int[10];}
};int main()
{//new/delete和malloc/free最大的区别就是//对于自定义类型除了开空间还会调用构造和析构函数A* Pa1 = (A*)malloc(sizeof(A));//malloc不会自动调用构造函数,必须得自己手动实现一个Pa1->set();A* pa2 = new A(10);//new支持分配空间时同时初始化A* pa3 = new A;//若申请空间时不进行初始化,将会调用默认构造函数Pa1->~A();//使用free之前需要自己调用析构函数,防止内存泄露free(Pa1);delete pa2;delete pa3;//使用delete在释放空间之前会先调用pa2的析构函数return 0;
}

该代码运行结果如下:
在这里插入图片描述

是的,在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc和free不会

operator new与operator delete函数

new 和delete 是用户进行动态内存申请和释放的操作符operator new 和 operator delete是系统提供的全局函数,new在底层调用operator new来申请空间,delete在底层调用operator delete全局函数来释放空间。
下面是库中operator new和 operator delete的源码:

/* operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;
申请空间失败,尝试执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否
则抛异常。 */
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: 该函数最终是通过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的实现*/
#define free(p)  _free_dbg(p, _NORMAL_BLOCK)

通过观察上述两个全局函数的实现知道,operator new实际上也是通过malloc来申请空间,如果malloc申请成功就直接返回,否则执行用户提供的空间不足的应对策略,如果用户提供该策略就继续申请,否则抛异常。operator delete最终也是通过free来释放空间的。


可能有人会问,为什么不直接底层用malloc和free来实现new和delete操作符,而还要再封装一层operator new和operator delete函数呢?

那是因为c++语言中对于程序运行中出现的问题采用的是抛异常的方法,而对于c语言内置的malloc和free函数,其在内存申请失败仅仅返回空指针,这有点不符合c++的要求,所以才需要在malloc的基础上再次进行了封装。

new和delete操作符的实现原理

对于内置类型来说,new/delete和malloc/free基本是相似的,需要注意的一点是:
new/delete申请和释放的是单个元素的空间,而new[]和delete[]申请的是连续空间。


自定义类型

  • new的原理
    1. 调用operator new函数申请空间
    2. 在申请的空间上执行构造函数,完成对象的构造
  • delete的原理
    1. 在空间上执行析构函数,完成对象中资源的清理。
    2. 调用operator delete函数释放对象的空间。

在这里插入图片描述

  • new T[N]的原理
    1. 调用operator new[]函数,在operator new[]中世纪调用operator new函数完成N个对象空间的申请
    2. 在申请的空间上执行N次构造函数
  • delete[] 同理

通过观察反汇编,可以看到new操作符确实调用了operator new函数和构造函数
在这里插入图片描述

malloc/free 和 new/delete 的区别

最后,我们来总结一下这两类的区别把!

共同点: 都是从堆上申请空间,并且需要用户手动释放。

不同点:

  1. malloc 和 free 是函数,new 和delete是操作符
  2. malloc申请的空间不会初始化,new可以初始化
  3. malloc申请空间是需要手动计算空间大小并传递,new只需在其后跟上空间类型即可,如果是多个对象,[]中指定对象个数即可
  4. malloc的返回值为void*,在使用是必须进行强转,new不需要
  5. malloc申请空间失败是返回NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
  6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数和析构函数,而new在折你去哪个空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理。

以上便是所有区别啦,对于这些区别,大家一定不要死记硬背,而是要理解的去记忆,这样才能有更好的效果!


对于c&c++内存管理方面的知识就到此结束啦!如果博主有哪些地方没有讲明白或者有错误的话,欢迎评论区指出哦!😘


http://www.ppmy.cn/news/87545.html

相关文章

jQurey-基本知识点总结

&#xff08;一&#xff09;jQurey基础知识 1、官网下载&#xff1a;jQuery jQurey是一个js文件&#xff0c;直接存到项目文件中&#xff0c;然后跟平常文件js导入一致&#xff1a; <script src"js/jquery-3.7.0.js"></script> 2、jQurey语法 jQure…

股票量化分析工具QTYX使用攻略——挖掘主升浪中的人气个股(更新2.6.5)

搭建自己的量化系统 如果要长期在市场中立于不败之地&#xff01;必须要形成一套自己的交易系统。 行情不等人&#xff01;边学习边实战&#xff0c;在实战中学习才是最有效地方式。于是我们分享一个即可以用于学习&#xff0c;也可以用于实战炒股分析的量化系统——QTYX。 QTY…

ChatGPT应用组队学习来了!

Datawhale学习 联合主办&#xff1a;Datawhale、百度文心 Datawhale联合百度文心&#xff0c;五月为大家带来AIGC应用专题&#xff1a;大模型从入门到应用&#xff0c;学习大纲如下&#xff08;文末整理了这次学习的所有资料&#xff09;&#xff1a; 参与学习 ▶ 活动时间&am…

c++ 11标准模板(STL) std::map(四)

定义于头文件<map> template< class Key, class T, class Compare std::less<Key>, class Allocator std::allocator<std::pair<const Key, T> > > class map;(1)namespace pmr { template <class Key, class T, clas…

Raft集群变更:This article is all your need

Background 为了变化raft集群&#xff0c;我们可以选择&#xff1a;.停在旧配置&#xff0c;然后再上线新配置 。但是这个会导致整个集群变得不可用&#xff0c;同时手动修改也会到来问题。 所以我们采用热变更 这也导致了安全性的问题&#xff0c;变更过程有可能导致两个le…

极米十年巅峰之作极米Z7X,能带走的百吋大屏

近年来&#xff0c;随着人们娱乐消费的升级&#xff0c;家用投影仪消费市场不断扩大&#xff0c;增长速度也非常可观。据IDC最新发布的数据显示&#xff0c;2022年中国投影机市场总出货量505万台&#xff0c;同比增长7.4%。其中&#xff0c;智能投影机市场&#xff08;搭载有OS…

软考初级程序员上午单选题(19)

1、操作数“00000101”与“00000101”执行逻辑______操作后&#xff0c;运算结果应为“00000000”。 A&#xff0e;或 B&#xff0e;与 C&#xff0e;异或 D&#xff0e;非 2、下列叙述中&#xff0c;属于预防计算机病毒的是______。 A&#xff0e;将来历不明的U盘换一台计算机…

2023年第十五届“中国电机工程学会杯”全国大学生电工数学建模竞赛题目 A题:电采暖负荷参与电力系统功率调节的技术经济分析

A题:电采暖负荷参与电力系统功率调节的技术经济分析 建设以新能源为主体的新型电力系统是应对全球气候变化挑战的重要举措。高比例新能源接入导致电力系统调节能力稀缺&#xff0c;亟需开发新的调节资源&#xff0c;如火电深度调峰、建设抽水蓄能电站、配置储能和挖掘负荷中的…