在阅读《STL源码剖析》的时候,突然发现一直以来认为的new
只有用来在堆上构造对象的用途,但实际上在allocator
分配内存的时候,也是利用new
来实现的。换句话说,new/delete
的本质是内存管理的一组操作。
在网上看到了侯捷先生的池內春秋—— Memory Pool 的設計哲學與無痛運用一文,让我对C++中内存管理有了较新的认识。
C++ 平台提供的内存配置工具
配置 | 释放 | 归属 | 可否重载 |
---|---|---|---|
malloc() | free() | C 标准函数 | 不可 |
new | delete | C++ 运算式 | 不可 |
::operator new() | ::operator delete() | C++ 运算子 | 可 |
alloc::allocate() | alloc::deallocate() | C++ 标准程式库之内存配置器 | 可自由设计并装载于任何容器上提供服務 |
四种配置方式示例:
void* p1 = malloc(512);
free(p1);
CRect* pr = new CRect;
delete pr;
void* p2 = ::operator new(512);
::operator delete(p2);
// 以下是 C++ 標準程式库中的配置器。其介面雖有標準規格,但各廠商多未遵守。
// 以致下面三种型式各異。
#ifdef _MSC_VER
// 以下兩者都是non-static,所以一定要借由物件调用
int* p3 = allocator<int>().allocate(512, (int*)0);
allocator<int>().deallocate(p3,512);
#endif
#ifdef __BORLANDC__
//以下兩者都是non-static,所以一定要借由对象调用
int* p3 = allocator<int>().allocate(512);
allocator<int>().deallocate(p3,512);
#endif
#ifdef __GNUC__
// 以下两者都是static,可通过全名调用
void* p3 = alloc::allocate(512);
alloc::deallocate(p3,512);
#endif
对于一条语句Complex* pc = new Complex(1,2);
编译期会视为如下代码块:
Complex *pc;
try {
void* mem = ::operator new( sizeof(Complex) ); // 配置
pc = static_cast<Complex*>(mem); // 轉型
pc->Complex::Complex(1,2); // 构造
// 注意:只有編譯器才能夠像上面那樣直接调用构造函数
}
catch( std::bad_alloc ) {
// 内存配置失敗,不執行构造函数
}
相应的,对于delete pc;
,编译期会执行:
pc->~Complex(); // 析构
::operator delete(pc); // 释放内存
在这四组接口中,唯有第三組(::operator new/delete()
)允許被重載、第四組(alloc::allocate/deallocate()
)允許由厂商(或客戶端程式員自己)发挥。因此党我们打算设计一个更高效的内存配置器時,关键便著落在第3、4组接口上。
未完待续