一、内存分布
首先我们应该知道各种数据在内存中的分布
栈:又叫堆栈,非静态局部变量/函数参数/返回值等等,栈是向下增长的。
内存映射段:高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享内存,做进程间通信。
堆:用于程序运行时动态内存分配,堆是向上增长的
数据段:存储全局数据和静态数据
代码段:可执行代码和只读常量
二、C语言中的动态内存管理方式
malloc:只申请空间
calloc:申请空间,并初始化(按位初始化为0)
relloc:改变空间的大小,可以扩大也可以减小
free:释放动态申请的空间(堆上的空间)
三、C++内存管理方式
C语言中的内存管理方式在C++中仍可以使用,但有些地方就无能为力而且用起来比较麻烦,因此C++提出了新的内存管理方式:通过new和delete操作符进行动态内存管理
void test()
{
// 申请一个int类型的空间
int *ptr = new int;
// 动态申请一个int类型的空间,并初始化为10
int *ptr1 = new int(10);
// 动态申请5个int类型的空间
int *ptr2 = new int [5];
delete ptr4;
delete ptr1;
delete[] ptr2;
}
注:
- 申请和释放单个元素的空间,使用new/delete;申请和释放连续的空间,使用new[]/delete[]
- 在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数;而malloc和free不会调用
四、operator new与operator delete函数
new和delete是用户进行动态内存分配的操作符,operator new和operator delete是系统提供的全局函数,new在底层调用operator new来进行内存分配,delete在底层通过operator delete进行释放空间。
operator new函数:该函数通过malloc来申请空间,当malloc申请空间成功后直接返回;申请空间失败,尝试执行空间不足应对措施,如果应对措施用户自定义了,则继续申请,否则抛异常。
operator delete函数:该函数最终通过free来释放空间
new的实现:malloc + 异常处理
delete的实现:析构 + free
五、new和delete的实现原理
-
内置类型:与malloc与free相似;不同:new/delete是申请/释放单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请失败后抛异常,malloc申请失败,返回NULL。
-
自定义类型:
new的原理:- 调用operator new函数申请空间
- 在申请的空间上执行构造函数,完成对象的构造
delete的原理:
- 在空间上执行析构函数,完成对象资源的清理工作
- 调用operator delete释放对象的空间
new T[N]的原理:
- 调用operator new[] 函数,在operator new[]中实际调用的operator new函数完成N个对象空间的申请
- 在申请的空间上执行N次构造
delete T[N]的原理
- 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
- 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间
六、定位new表达式
使用格式:new(place_address) type 或 new(place_address) type (initializer-list)
使用场景:定位new表达式在实际中一般搭配内存池使用,因为内存池分配的内存没有初始化
注:不经常使用,对已经分配好的内存空间,显示调用构造函数进行初始化
七、常见的面试题
malloc/free和new/delete的区别
- malloc和free是函数,new和delete是操作符
- malloc申请的空间不会初始化,new申请的空间可以初始化
- malloc申请空间时需要手动计算空间大小并传递,new只需要在其后跟上空间的类型即可
- malloc的返回值为void*,在使用时必须强转,new不需要,因为new后面跟的是空间类型
- malloc申请失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
- 申请自定义类型对象时,malloc/free只会申请/销毁空间,不会调用构造/析构函数;而new在申请空间时会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理
- new/delete比malloc/free的效率稍微低一点,因为new/delete在底层封装了malloc/free
内存泄漏:
内存泄漏指因为疏忽或错误造成程序未能及时释放已经不再使用的内存。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误失去了对该段内存的控制,因而造成了内存的浪费。
常见的内存泄漏:
1. 未及时使用delete或free释放
2. 因为异常导致delete或free没有执行(非常难发现)
解决方式:
1. 养成良好的代码风格
2. 利用RAII思想或智能指针管理