1.
new operator:new操作符,用于动态分配内存并进行初始化;
1)调用operator new 获得一块可用内存的地址
2) 调用constructor,为获得的内存中的对象设定初值(初始化对象)
new operator是不能被重载的,无论怎样,它总进行这两个步骤。可以被重载的是operator new,即用来获取可用内存的函数,通过重载operator new可以简介优化new operator。
string *ps = new string("hello world");
可以看出 new 和 malloc 还是有点不同的,malloc 申请完空间之后不会对内存进行必要的初始化,而 new 可以。
当编译器看到上面代码,必须产生一些代码,反映以下行为:(new所完成的事)
void* rawMemory=operator new(sizeof(string));//取得原始内存地址,放置一个string对象
call string::string("hello word") on *rawMemory //将内存中对象初始化
string *ps=static_cast<string*>(rawMemory);//让ps指向新完成的对象
2.
operator new:标准库的函数,只分配内存(同malloc一样)不进行初始化(或者传递一个可用的内存地址),可以自己进行重载(加上额外参数,但第一参数必为size_t),也可以主动调用。
void *operator new(size_t size);
函数返回指针,类型为void*,指向一块原始的未设初值的内存。size_t表示要分配的内存大小。
调用示例:
void* rawMemory=operator new(sizeof(string));//operator new返回指针,指向一块足够容纳一个string对象 的内存
对于operator new,其用法根据new operator的用法而有所不同:
刑如普通的new用法”A *p=new A“,那么内存实际上由operator new进行分配并传回一个地址,
形如placement new的用法”A *p=reinterpret_cast<A*>(new(ptr) A())",那么operator new基本上不做任何事情,只是将传入的ptr(指向一块可用内存的指针)参数返回而已,供接下来的构造函数使用。
也就是说普通new表达式自己分配内存并构造对象,placement new表达式在已分配好的内存上构造对象(这也就是为什么1 1)说new operator"获得"可用的内存地址而不是“分配内存",因为在placement new的使用下,operator是不分配内存的),但无论怎样,都要进行1所说的两个动作。
3.
placement new(定位new):new operator的另外一种用法 ,在已分配的内存上构造对象;
要使用#include <new> #include <new.h>
class Widget{
public:
Widget(int widgeSize);
...
};
Widget *constructWidgeInBuffer(void *buffer,int widgetSize)
{
return new(buffer) Widge(widgetSize);//placement new
}
此函数返回指针,指向Widget对象,该对象被构造于传递给此函数的一块内存缓冲区上。
表达式new(buffer) Widge(widgetSize)只是new operator的用法之一(placement new是new operator 的一种用法),指定额外自变量buffer作为new operator“隐式调用operator new”时所用。于是,被调用的operator new除接受“一定得有的size_t参数”还接受void*,指向一块内存,准备接受构造好的对象:
void* operator new(size_t,void *location)
{
return location;
}
由上代码,placement new唯一要做的就是将它获得的指针再返回。
对于placement new中的operator new,第一个size_t类型的参数其实是没有用的。它的主要功能是传递一个可用内存的地址。
注意:new operator是操作符,placement new是这个操作符的一种用法,而operator new是标准库中的函数,new operator调用了 operator new。
4、
delete operator<--->operator delete 就好比new operator<--->operator new
string *ps;
delete ps;//使用delete operator,析构ps所指对象,释放该对象所占内存(由operator delete执行)
void operator delete(void *memoryToBeDeallocated);
delete ps;编译器会产生:
ps->~string();//调用对象析构函数
operator delete(ps);//释放对象所占内存
若只处理原始的、未设初值的内存:
void *buffer=operator new(50*sizeof(char));//分配足够内存,存50个char,没调用任何constructor
...
operator delete(buffer);//释放内存,没调用任何destructor
以上相当于调用malloc和free。
若使用placement new在某内存块中产生对象,应避免对那块内存使用delete operator(因为该内存内含的对象不是由operator new分配来的),应直接调用该对象的析构
void *mallocShared(size_t size);
void freeShared(void *memory);
void *sharedMemory=mallocShared(sizeof(Widget));
Widget *pw=constructWidgeInBuffer(sharedMemory,10);//placement new用法
...
delete pw;//错。sharedMemory来自mallocShared,不是来自operator new
pw.~Widget();
freeShared(pw);
5、
string *ps=new string[10];
内存以operator new[]分配,
operator new[]:operator new 的数组版本,与new操作符创建数组的用法对应,operator new[]与operator new有一定区别,“重载的operator new[]必须具有返回类型void*,并且接受的第一个形参类型为size_t,用表示存储特定类型给定数目元素的的数组的字节数值自动初始化操作符的size_t形参”,注意,operator new[]返回的地址之前还有4个字节用来存储元素数目(编译器背着我们做的,就算自己重载了也一样),
数组版new operator必须针对数组中每个对象调用 一个constructor:
string *ps=new string[10];
//调用operator new[]分配足够容纳10个string对象的内存。再针对每个元素调用string default constructor
当delete operator被用于数组,针对数组中每个元素调用其destructor,再调用operator delete[]释放内存:
delete []ps;
//为数组中每一元素调用string 析构函数,再调用operator delete[]释放内存
operator delete[]可以被重载
总结:
1) placement new(定位new)是new operator的另一种使用方法,用于在已分配好的内存上构造对象,它与普通的new表达式调用的是不同的operator new。
2) operator new 只分配内存而不构造对象,对象的构造,析构以及内存的释放由自己负责,提升了效率也加重了负担。
3) operator new 的使用可以与operator delete的使用相配合,new operator的使用与delete operator的使用相配合,但使用operator new分配的单个的内存也可以与delete operator配合使用;
operator new[]的使用绝对不可以与delete []相配合,也就是说delete []只能释放经由new operator得到的连续内存,否则会发生未定义行为!
4) operator new[]直接调用与经由new operator调用效果是不同的,直接调用operator new[]相当于调用operator new,而调用new operator构造数组编译器还做了其他事情以便于与delete []的使用相配合。