cpp提供了不同层次的内存管理工具,如malloc、new和allocator,所有的管理工具最终都是调用malloc,同时malloc最底层是调用系统的内存管理工具API。
关键点:cpp中提供给程序员的内存管理工具有malloc、new和allocator。
allocator是给容器使用,只定义了实现接口的标准,具体实现方式没有要求,现在很多编译器allocator的实现仅仅是对new和delete做了一层简单的封装,效率低,之前版本的gnuc中使用了内存池+malloc的方式实现了高效的内存管理。
new实现内存管理调用了operator new进行内存分配+placement new实现对象构造。operator new可以由用户进行重载,由此实现内存管理的接管,cpp中全局的operator new底层是malloc实现。
内存管理的两个关键点:1.了解内存管理是如何调用到底层的malloc的;2.实现operator new的重载和allocator的自定义来实现内存管理接管。
概念了解
cpp内存管理malloc、new、operator new、allocator。
malloc不可被重载,new是cpp的关键字,由cpp标准确定下来了也不能重载,operator new可以被重载,也是接管内存管理的方式,allocator可以自定义然后将自定义版本传递给容器,也能实现内存管理的接管。
一般不直接使用allocator来进行内存分配,因为使用起来比较麻烦,并且需要程序员自己记住分配的内存大小。
malloc、new、allocator分配内存实例
1 | void* p1 =malloc(512); |
new expression的底层实现
1 | Complex* pc = new Complex(1, 2); |
1 | void* mem = operator new(sizeof(Complex)); // 调用operator new分配内存,operator new的底层是的调用malloc |
operator new可以被重载,便能实现内存管理的接管,至于operator new的底层如何实现,标准库没有要求,但是目前普遍使用malloc来实现内存管理。同时operator new考虑分配失败时调用用户定义的handler函数,确保malloc能分配到内存或者实现分配不到内存返回0让malloc退出。仅仅只是对malloc做了一点简单的封装。
对于上述的placement new实现,底层实现如下
1 | new(pc)Complex(1, 2); |
接下来是对配套使用的delete pc进行解析
1 | delete pc; |
1 | pc->~Complex(); // 可以直接调用构造函数,因为pc现在指向了Complex对象 |
tips: 析构函数的意义主要是对程序员分配的堆空间(调用new或者malloc)进行回收,以及调用成员的析构函数来实现成员变量的堆空间的释放。换言之,如果对象自身没有分配堆空间,成员变量也没有分配堆空间,那么析构函数是没有用的。当然除了物理资源的管理,析构函数也常常用来实现其它资源管理,如互斥锁的释放,以及计数器的减小(如智能指针的实现)。
vector分配器底层还是调用malloc,其数据保存还是放在堆区,同手动malloc分配方式。
allocator的底层实现
1 | T* p = allocate(); |
1 | // 上述代码底层调用 |
总结
可以发现,无论是new expression还是allocator.allocate(),底层都是调用::operator new,底层再去调用malloc,同理相反的delete和deallocate()底层都是调用::operator delete,底层再去调用free。因此如果实现了对operator new的重载,便接管了内存管理!而对于内存释放operator delete一般不去重载。