C++内存管理
内存分配层面
学习内存管理主要是为了提高使用内存的效率和速度
一般而言,C++应用程序使用容器或使用new来申请内存,它们的底层实现都是malloc,很少直接与操作系统API打交道
Primitives
空间配置器allocator严格来说不算重载,应*设计来搭配容器
四原语实例
operator new作用域是在全局的
使用空间配置器allocator需要指定分配空间大小,释放时也需要指定,容器适用此方法
new
new有两个动作,一个是分配内存,一个是调用构造函数
new expression
一个new表达式如下
Complex* pc = new Complex(1, 2);
编译器把这行语句转化为
Complex *pc;
try{
void* mem = operator new( sizeof(Complex) ); //1, allocate
pc = static_cast<Complex*>(mem); //2, cast
pc->Complex::Complex(1, 2); //3, constructor
}
catch(std::bad_alloc){
//
}
分配内存在第1行,operator new可重载,底层实现是malloc
第2行把指针转型
第3行调用构造函数,VC6可以通过,其他编译器会失败
要直接调用构造函数,可以使用placement new:
new( p)Complex(1, 2);
对应地,delete表达式:
delete pc;
编译器会转化为
pc->~Complex(); //先析构
operator delete(pc); //然后释放内存
可以直接调用析构函数
operator delete底层是free
array new
array new例子:
Complex* pca = new Complex[3];
//调用三次constructor
//无法由参数给予初值
delete[] pca;
//调用三次destructor
构造的pca布局如上图所示,其中cookie包含pca的长度,所有的C++编译器平台设计的malloc和free中的cookie都是如此。
当array new对应的是delete,如果对象没有destructor或destructor是trivial的,则不会发生内存泄漏,否则的话(对象的destructor有意义并且重要)会发生内存泄漏
比如:
int* pi = new int[10];
delete pi; //array new与delete对应,这样写可以,因为int没有destructor
string* psa = new string[3];
delete psa; //错误,只会调用一次destructor,发生内存泄漏
并且根据对象的不同,其内存布局是不一样的
placement new
placement new允许将对象建构于已经分配的内存中,形式如下:
#include <new>
char* buf = new char[sizeof(Complex) * 3];
Complex* pc = new(buf)Complex(1, 2);
delete [] buf;
代码首先申请3个Complex大小的内存,用buf指针指向,然后在buf上创建对象
改变内存分配机制
我们已经知道new和malloc是不能重载的,所以可以通过重载operator new来改变内存分配机制
内存分配调用过程如下:
我们可以重载global functions,来改变内存分配机制,比如在类里重载
Foo::operator new(size_t);
Foo::operator delete(void*);
改变内存分配机制主要是为了去除cookie