C++中的动态内存管理
1. 内存分布与虚拟地址空间
每一个加载到内存中的进程,都有一个虚拟地址空间,再经过页表映射到物理内存空间。
2. C语言的动态内存管理
malloc:动态开辟空间,不会初始化。
calloc:动态开辟空间+初始化。
realloc:堆动态开辟的空间进行重新分配。
free:释放动态开辟的空间。
上面这些都是函数调用。
3. C++的动态内存管理
new:动态申请内存
delete:释放内存
批量动态内存管理:new [] ,delete []
int main() { int* a = new int; int* b = new int[10]; // new 10个int类型的元素 int* c = new int(10); // 初始化为10 delete a; delete[] b; delete c; return 0; }
new和delete是关键字!!
new在申请自定义类型的空间时,在申请空间后会自动调用其构造函数进行初始化;delete在释放自定义类型的空间时,会先调用它的析构函数,再释放空间。
4. new和delete的实现原理
1. operator new 和 operator delete函数
new和delete是用户进行动态内存申请和释放的操作符(关键字),而operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来动态申请空间,delete在底层通过operator delete全局函数来释放空间。
实际上,operator new 是通过调用malloc来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足时的应对措施,如果用户提供该措施就继续申请,否则就抛异常。operator delete 最终也是通过调用free来释放空间的。
operator new 和 operator delete函数对于自定义类型不会调用其构造/析构函数。
2. 重载operator new 和 operator delete函数
一般情况下不需要对 operator new 和 operator delete进行重载,除非在申请和释放空间的时候有某些特殊的需求。比如:在使用new和delete申请和释放空间时,打印一些日志信息,或者使用内存池分配空间,不需要operator new直接访问堆。
3. new和delete的实现原理
new:先调用 operator new申请空间(operator new又调用malloc ),然后对于自定义类型会调用它的构造函数初始化。
delete:对于自定义类型先调用其析构函数释放资源,然后调用 operator delete函数释放空间(operator delete又调用 free)。
new [N]:先调用 operator new[]申请空间(operator new[]调用operator new ),然后对于自定义类型会调用N次它的构造函数初始化。
delete []:对于自定义类型先调用N次其析构函数释放资源,然后调用 operator delete[]函数释放空间(operator delete[]调用 operator delete)。
5. placement-new
placement-new是指在已分配的动态内存空间中调用构造函数初始化一个对象。 一般是配合内存池使用,因为内存池申请的内存并没有初始化,所以使用placement-new去初始化自定义类型。
使用方式:
class T { public: T(int a) :a_(a) {} ~T() {} private: int a_; }; int main() { // 正常 new, delete T* t = new T(10); delete t; // 只申请空间,并没有初始化! T* t1 = (T*)malloc(sizeof(T)); assert(t1); // 使用 placement-new 初始化 t1 new(t1)T(20); t1->~T(); free(t1); return 0; }
6. malloc/free 和new/delete的区别(面试题)
malloc/new是函数调用,而new/delete是关键字。new申请空间可以进行初始化。(不过calloc也可以)malloc要传参指定需要的空间大小,而new只需要给类型就可以。malloc的返回值是void*,要进行强转,而new不需要,直接返回对应类型的指针。malloc申请空间失败返回NULL,而**new失败抛异常**。- 对于自定义类型的区别!!
异常捕获:
int main() { try { // 10G int* p = new int[1024*1024*1024*10]; // ... } catch(const exception& e) // 捕获异常信息 { cout << e.what() << endl; // 打印异常信息 } return 0; }
7. 内存泄漏
一般有两类内存泄漏:
- 动态申请的内存空间没有释放,指向这块内存的指针丢了。
- 申请的系统资源没有释放!如文件描述符
fd,网络套接字socketfd等。
避免内存泄漏:
- 良好的代码规范。
- 使用
RAII来管理资源。(智能指针) - 使用内存泄漏检测工具。
- 动态申请的内存空间没有释放,指向这块内存的指针丢了。
- 申请的系统资源没有释放!如文件描述符
fd,网络套接字socketfd等。
避免内存泄漏:
- 良好的代码规范。
- 使用
RAII来管理资源。(智能指针) - 使用内存泄漏检测工具。
最后挂个链接,欢迎一起学习,一起进步!https://xxetb.xet.tech/s/4G6TWG


