首先咱们区分下几个容易混淆的关键词:html
new、operator new、placement new
new和delete操做符咱们应该都用过,它们是对堆中的内存进行申请和释放,而这两个都是不能被重载的。要实现不一样的内存分配行为,须要重载operator new,而不是new和delete。ios
operator new
(1) 只分配所要求的空间,不调用相关对象的构造函数。当没法知足所要求分配的空间时,则
->若是有new_handler,则调用new_handler,不然
->若是没要求不抛出异常(以nothrow参数表达),则执行bad_alloc异常,不然
->返回0
(2) 能够被重载
可是不能在全局对原型为void operator new(size_t size)这个原型进行重载,
通常只能在类中进行重载。若是类中没有重载operator new,
那么调用的就是全局的::operator new来完成堆的分配。
同理,operator new[]、operator delete、operator delete[]也是能够重载的。
(3) 重载时,返回类型必须声明为void*
(4) 重载时,第一个参数类型必须为表达要求分配空间的大小(字节),类型为size_t
(5) 重载时,能够带其它参数
new(也称做new operator) (1) 调用operator new分配足够的空间,并调用相关对象的构造函数 (2) 不能够被重载
placement new 是重载operator new的一个标准、全局的版本。它不能被自定义的版本代替(不像普通的operator new和operator delete可以被替换成用户自定义的版本)。它的原型以下:
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{ 数组
// try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0)
if (_callnewh(size) == 0)
{ 函数
// report no memory
static const std::bad_alloc nomem;
_RAISE(nomem);
}this
return (p);spa
}指针
placement new,只是operator new的一个重载的版本,只是咱们不多用到它。若是你想在已经分配的内存中建立一个对象,使用new时行不通的。也就是说placement new容许你在一个已经分配好的内存中(栈或者堆中)构造一个新的对象。原型中void*p实际上就是指向一个已经分配好的内存缓冲区的的首地址。code
咱们知道使用new操做符分配内存须要在堆中查找足够大的剩余空间,这个操做速度是很慢的,并且有可能出现没法分配内存的异常(空间不够)。placement new就能够解决这个问题。咱们构造对象都是在一个预先准备好了的内存缓冲区中进行,不须要查找内存,内存分配的时间是常数。并且不会出如今程序运行中途出现内存不足的异常。因此,placement new很是适合那些对时间要求比较高,长时间运行不但愿被打断的应用程序。 placement new使用方法以下:htm
1)分配内存
栈上:
char buf[ sizeof(Foo) ]={0};
2)构建对象
3)使用对象
4)析构对象,显式的调用类的析构函数。
5)销毁内存
placement new的好处:
1)在已分配好的内存上进行对象的构建,构建速度快。
2)已分配好的内存能够反复利用,有效的避免内存碎片问题。 对象
Placement new的标准用法:
#include <iostream> using namespace std; class Foo { char cc; float f; public: void print() { std::cout << "ADDR: " << this << std::endl; } void set_f( float _f ) { std::cout << "set f val : " << _f << std::endl; f = _f; } void get_f() { std::cout << "get f val : " << f << std::endl; } ~Foo(){ cout <<"~Foo()"<<endl; } }; int main() { char* buff = new char[ sizeof(Foo) ]; memset( buff, 0, sizeof(Foo) ); Foo *pfoo = new (buff)Foo; pfoo->print(); pfoo->set_f( 1.0f ); pfoo->get_f(); pfoo->~Foo(); delete [] buff; return 0; }
placement new还能够解决的一个问题是创建带参数的构造函数对象数组
#include <iostream> using namespace std; class CPong { public: CPong( int m ) : v(m) { std::cout << "CPong ctor." << std::endl; } private: int v; }; int main() { char* pong = new char[ sizeof(CPong) * 10 ]; CPong* pp = (CPong*)pong; for ( int i=0; i<10; ++i ) { new (pp+i)CPong(i); } for ( int j=0; j<10; ++j ) { pp[j].~CPong(); } delete [] pong; return 0; }
注意:
在C++标准中,对于placement operator new []有以下的说明: placement operator new[] needs implementation-defined amount of additional storage to save a size of array。因此咱们必须申请比原始对象大小多出sizeof(int)个字节来存放对象的个数,或者说数组的大小。
使用方法第二步中的new才是使用placement new,实际上是没有申请内存的,只是调用了构造函数。返回一个指向已经分配好的内存的一个指针,因此对象销毁的时候不须要调用delete释放空间,但必须调用析构函数销毁对象。
参考自:http://www.ksarea.com/articles/20080124_cc.html
operator new分为三种形式(前2种不调用构造函数,这点区别于new operator):
1: void* operator new (std::size_t size) throw (std::bad_alloc); 2: void* operator new (std::size_t size, const std::nothrow_t& nothrow_constant) throw(); 3: void* operator new (std::size_t size, void* ptr) throw();
第一种分配size个字节的存储空间,并进行对象类型进行内存对齐。若是成功,返回一个非空的指针指向首地址。失败抛出bad_alloc异常。
第二种在分配失败时不抛出异常,它返回一个NULL指针。
第三种是placement new版本。它不分配内存,调用合适的构造函数在ptr所指的地方构造一个对象,以后返回实参指针ptr。
三种版本的operator new 定义在全局命名空间,不在std中。第1、第二个版本C++默认在每一个编译单元中声明,不须要#include <new>头文件。第1、第二个版本能够被用户更换和重载,定义本身的版本,第三种placement new不可重载。
// operator new example #include <iostream> #include <new> using namespace std; struct myclass {myclass() {cout <<"myclass constructed\n";}}; int main () { int * p1 = new int; // same as: // int * p1 = (int*) operator new (sizeof(int)); int * p2 = new (nothrow) int; // same as: // int * p2 = (int*) operator new (sizeof(int),nothrow); myclass * p3 = (myclass*) operator new (sizeof(myclass)); // (!) not the same as: // myclass * p3 = new myclass; // (constructor not called by function call, even for non-POD types) new (p3) myclass; // calls constructor // same as: // operator new (sizeof(myclass),p3) return 0; }
重载operator new
class Base { public: Base() { } void *operator new( size_t size, string str ) { cout << "Logging an allocation of " << size << " bytes for new object'" << str << "'" << endl; return malloc( size ); } int var; double var2; }; ... Base* b = new ("Base instance 1") Base;
new operator的四种用法
pointer = new type; pointer = new type( initializer ); pointer = new type[size]; pointer = new( arg-list ) type... //4th vision Foo *foo; foo = new(nothrow) Foo(); //use the 4th vision assert( foo );
其中,nothrow为 C++预约义的std::nothrow_t类型全局常量。
operator new能够被重载,它的三个版本本质上是函数,它们只分配空间,不调用构造函数。而new、delete(也叫作new operator、delete operator)不可被重载,它们是运算符,分配空间,而且调用构造函数。