1. new operator:new操作符,用于动态分配内存并进行初始化;
placement new(定位new):new operator的另外一种用法 ,在已分配的内存上构造对象;
operator new:标准库的函数,只分配内存不进行初始化(或者传递一个可用的内存地址),可以自己进行重载,也可以主动调用。
注意:new operator是操作符,placement new是这个操作符的一种用法,而operator new是标准库中的函数,new operator调用了 operator new。
2. new operator 实际进行两个动作:1)调用operator new 获得一块可用的内存地址 2) 初始化对象
new operator是不能被重载的,无论怎样,它总进行这两个步骤,可以被重载的是operator new,即用来获取可用内存的函数,通过重载operator new可以简介优化new operator。
3. 对于operator new,其用法根据new operator的用法而有所不同,如果是形如”A *p=new A“的普通的new用法,那么内存实际上由operator new进行分配并传回一个地址,如果是形如”A *p=reinterpret_cast<A*>(new(ptr) A())"的定位new的用法,那么operator new基本上不做任何事情,只是将传入的ptr(指向一块可用内存的指针)参数返回而已,供接下来的构造函数使用。
也就是说普通new表达式自己分配内存并构造对象,定位new表达式在已分配好的内存上构造对象(这也就是为什么2说new operator"获得"可用的内存地址而不是“分配内存",因为在定位new的使用下,operator是不分配内存的),但无论怎样,都要进行2所说的两个动作。
要注意的是默认的(即全局作用域中的)的operator 一共有两个重载版本,对应于普通new表达式和定位new的两个用法,如果要对operator new进行重载,那么由于作用域覆盖原则,重载的operator new会覆盖全局作用域中的两个operator new,所以重载的时候务必要重载两个(如何重载见4),否则要不默认的new表达式无法使用要不定位new无法使用。当然也可以在new操作符前加上::表示使用的是全局版本。
4. 重载的operator new必须具有void* 返回类型而且第一个参数必须为size_t类型(就算是int类型都不行),此外根据是定位new还是普通new,operator new决定是否增加指针参数(只要满足返回类型为void*而且第一个参数类型为size_t,其他参数随便加,只不过普通new表达式只调用只含有一个size_t参数的operator new版本,而定位new表达式只调用含有一个size_t类型和指针参数类型的operator new版本,自己重载的其他版本可以按需要添加参数)
对于定位new中的operator new,第一个size_t类型的参数其实是没有用的,它的主要功能是传递一个可用内存的地址。
5. 此外还有operator delete,operator new[]以及operator delete delete[],它们的作用与operator new稍有不同
operator delete: 用于delete操作符(delete操作符实际上也进行两个步骤,析构被delete的对象,调用operator delete释放内存)由于delete操作符的用法只有一种,因而operator delete的作用也就只有一种:释放内存
重载的operator delete必须具有返回类型void,它可以定义为接受单个void*类型形参,也可以定义为接受void* 和size_t类型两个形参,如果提供了size_t类型的形参,就有编译器用第一个形参所指对象的大小自动初始化size_t形参(但是第一个形参不是void*型么?void* 型是不具备所指对象大小的信息的,可能是编译器又在背后做了其他事情吧),对于编译器自动初始化size_t形参的做法,这种类似的做法已经出现过许多次了,比如定位new中的operator的第一个size_t形参,以及后自增操作符大的哑元参数等(编译器确实背着我们干了许多事)
operator new[]:operator new 的数组版本,与new操作符创建数组的用法对应,operator new[]与operator new有一定区别,“重载的operator new[]必须具有返回类型void*,并且接受的第一个形参类型为size_t,用表示存储特定类型给定数目元素的的数组的字节数值自动初始化操作符的size_t形参”,注意,operator new[]返回的地址之前还有4个字节用来存储元素数目(编译器背着我们做的,就算自己重载了也一样),代码示例如下(参考来源:http://bbs.chinaunix.net/thread-1853264-1-1.html,感谢liwangli1983的指引):
#include<iostream> using std::cout; using std::endl; class X { public: ~X() { cout << "del" << endl; } private: int a; int b; }; void *operator new[](size_t size) { void *p = malloc(size); cout << "size_t: " << size << endl; cout << "address: "<< p << endl; return p; } int main(void) { X *q = new X[5]; cout << "address\': "<< q << endl; cout << *(reinterpret_cast<int *>(q) - 1) << endl; delete [] q; system("pause"); return 0; }