内存划分
为什么要这些区域
根据不同的区的作用以及生命周期就可以解答
为了方便管理
每个区存放的内容和特点
堆区要重点关注(堆区是留给我们自主控制的,你来负责,也要负责收拾烂摊子,因为我们要负责自己开辟和销毁)
内存管理方式
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++的管理方式是操作符
优势
- 用法上变简洁了:自定义类型:如果要创建一个节点,还要去CreateListNode函数里面malloc然后检查开空间是否成功还要传值进去:现在只需要new ListNode(1);一次性把开空间和构造函数包含了
- 内置类型:可以初始化了;自定义类型支持开空间+构造函数(动刀子的核心原因:自定义类型不能很好地初始化)
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);//既调用了构造函数,也进行了初始化(单参数构造函数支持隐式类型转换)
- 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。
系统资源泄漏
指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。