当你写string *ps = new string(“Hands up!”)时,你所使用的new是所谓的new operator,它其实干了两件事:
一、分配足够的内存(实际大小是大于所创建的对象大小)
二、调用对象构造函数,为刚才分配的内存对象设定初值。
new operator永远干这两件事,无论如何你不能改变其行为。
上面的那段代码大约反映以下的行为:
void *mem = operator new(sizeof(string));
call string::string(“Hands up!”) on *mem;//只能由编译器完成,用户是不允许这样操作的,也就是说如果你想建立一个堆对象就必须用new操作符,不能直接像上面一样调用构造函数来初始化堆对象。
string ps = static_cast<string>(mem);
也就是说operator new仅仅分配内存(就像malloc一样),我们能够做的仅仅是重载operator new,为自己的类创建一个定制的内存管理方案,这也让我有点明白为什么在重载operator new的时候并没有写调用构造函数的代码,但它确实被调用了,原来都是new operator在背后操作。
编译器看到类类型的new或者delete表达式的时候,首先查看该类是否是有operator new或者operator delete成员,如果类定义了自己的new和delete函数,则使用这些函数为对象分配和释放内存,否则调用标准库版本。如果你想定制自己独有的内存分配过程,你应该重载全局的operator new函数,然后使用new操作符,new操作符会调用你定制的operator new。当然你可以显示的调用:: operator new和:: operator delete强制使用全局的库函数。
Placement new
placement new的作用是在一块分配好的原始内存上,构建对象。是一个特殊版本的operator new,成为placement new。
class Widget{
public:
Widget(int widgetSize);
...
};
Widget* constructWidgetInBuffer(void *buffer, int widgetSize)
{
return new (buffer) Widget(widgetSize);
}
这是new operator的用法之一,其中指定一个额外变量作为new operator“隐式调用 operator new”时所用。于是,被调用的operator new除了接受“一定得有的 size_t变量”外,还接受一个void*参数,指向一块内存,准备用来接受构造好的对象。像这样的operator new就是所谓的placement new.
void* operator new(size_t, void* location)
{
return location;
}
以下用法原理:
void *ptr = malloc(sizeof(Kamisato));
Kamisato *kamisato = new(ptr) Kamisato();
这样,编译器就像文章最开始写的那里那样,唯一不同是调用operator new的重载版本,在给定的这个指针(ptr)的位置直接调用构造函数。
void *ptr2 =::operatornew(sizeof(Kamisato), ptr);
Kamisato *kamisato = static_cast<Kamisato *>(ptr2);
kamisato->Kamisato::Kamisato();
事实上形式都没有变,就是多了个参数而已;因为operator的重载版本就是返回那个指针而已,这里的ptr2和ptr指向的是同一个内存地址。
用operator new分配的内存,请用operator delete释放。必须一一对应。
数组
数组的分配需要使用上面的兄弟版本 operator new[],它必须对数组中的每个元素调用其构造函数。