【C++】简单学——内存管理

发布于:2025-06-29 ⋅ 阅读:(18) ⋅ 点赞:(0)

内存划分

为什么要这些区域

根据不同的区的作用以及生命周期就可以解答

为了方便管理

每个区存放的内容和特点

堆区要重点关注(堆区是留给我们自主控制的,你来负责,也要负责收拾烂摊子,因为我们要负责自己开辟和销毁)

内存管理方式

C语言中的动态内存管理方式

malloc,calloc,realloc,free

realloc是异地扩容,然后原来的空间会被顺便释放

C语言的管理方式更趋近于函数

C++的动态内存管理方式

new,delete操作符

使用方法

int* p1 = new int;
int* p2 = new int[10]; // new 10个int对象
delete p1;
delete[] p2;//释放的delete要匹配

C++的管理方式是操作符

优势

  1. 用法上变简洁了:自定义类型:如果要创建一个节点,还要去CreateListNode函数里面malloc然后检查开空间是否成功还要传值进去:现在只需要new ListNode(1);一次性把开空间和构造函数包含了
  2. 内置类型:可以初始化了;自定义类型支持开空间+构造函数(动刀子的核心原因:自定义类型不能很好地初始化)
    int* p3 = new int(10); // new 1个int对象,初始化成10
    int* p3 = new int[10];//要区分好,这个是开是个int空间
    
    int* p4 = new int[10]{ 1,2,3,4,5 }; //后面没有初始化的都是0
    
    Date* d1 = new Date(13);//既调用了构造函数,也进行了初始化(单参数构造函数支持隐式类型转换)
  3. new失败了会自动抛异常,不用手动检查(C语言malloc开空间失败时会返回NULL,要检查,new会失败的时候自动抛异常)

operator new和operator delete函数

概念:系统提供的两个全局函数,作用跟malloc和free差不多

  • 库里面的全局函数
  • operator new是对malloc的封装,operator delete是对free的封装

自己用的话没什么用,真正的作用

  • malloc比较麻烦
  • 自定义类型的申请空间没有调用构造函数

operator new和operator delete存在的意义(实现new)

以下知识仅作为常识了解

问:如果说想要在申请内存的时候就直接调用构造函数,为什么不直接让new封装构造函数和malloc呢,为什么还要有operator new?即new = malloc + 构造函数

operator new会做的事情

  • 封装malloc,修改空间申请失败的返回值(C++要符合面向对象的处理方式(抛异常))operator new和malloc本质上没啥区别,区别在于处理失败的方式

operator new[ n ]会做的事情

  • 封装n个operator new,根据需求(是否为自定义类型),在开空间的时候,在头上多开一个int空间用来存放创建对象的数量(因为operator delete[ n ]在释放空间的时候没有传参,所以不知道调用调用多少次析构函数)

operator delete会做的事情

  • 就free

operator delete[ n ]会做的事情

  • 封装n个operator delete,(前提是自定义类型导致operator new[ n ]多开了四个字节)因为operator new[ n ]在空间的头上多开了四个字节(int),所以释放空间的时候会忘前偏移四个字节来释放空间

根据这个特性,如果说不匹配使用,会出现的结果就显而易见了

对于自定义类型:Date * d1 = new Date[10]; delete d1就会出问题(运行报错)

原因:(申请空间后,必须要从那块空间的开头来释放,不能放在中间)

operator new [ ]会在开头多开四个字节

new[10]的时候,d1返回的是第一个对象的位置的指针

delete[ ]的时候,会特地多往前偏移四个字节来释放

delete会直接从第一个对象位置的指针开始释放

原本正常情况下delete的时候指向的是第一个Date的开头,但是因为new[ ],编译器多开了四个字节,所以就会出现分段释放空间的问题,于是就报错了

特殊情况(编译器优化)

根据上面的情况可以知道,operator new[ n ]之所以会多开四个字节,是因为这是一个自定义类型并且还自己实现了析构函数,导致operator delete[ n ]需要知道调用析构函数的数量

假如我们没有写析构函数(编译器自己生成的析构函数什么事也不做),部分编译器为了优化,可能会让operator new[ n ]不必多开四个字节,operator delete[ n ]不必多偏移四个字节开始释放 

但这个情况具体得看编译器的激进程度,有些编译器不激进,会不做这个优化,operator new[ n ]仍然多开四个字节,operator delete[ n ]仍然多偏移四个字节开始释放 

所以前面有关的内容仅供常识了解即可

定位new

一般情况下,我们是没办法显示地调用构造函数的(析构函数可以显示调用)

不过一般不会显示调用析构函数,因为对象销毁的时候会自动调用,所以就会让同一块空间被释放两次

什么时候需要显示调用构造函数?没办法用new的时候

定位new:显示调用构造函数可以对一块已经有的空间进行初始化

这块空间如果是从内存池把内存申请出来(自身没有初始化),就可以用定位new调用构造函数初始化

(池化技术:提前储备内存,要的时候就能快点)

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

内存泄漏(广义来说是资源泄漏)

概念:一块空间已经没有再用了,但是却没有释放

一台机器长时间运行不关闭,然后每分每秒都在申请空间但是不释放,久了,系统就会崩

所以说重启解决百分之九十九的问题原因就是这个

内存泄漏分类

堆内存泄漏(Heap leak)

堆内存指的是程序执行中依据须要分配通过malloc/calloc/realloc/new等从堆中分配的块内存,用完后必须通过调用相应的 free或者delete 删掉。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生HeapLeak。


系统资源泄漏

指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。


网站公告

今日签到

点亮在社区的每一天
去签到