参考:
placement new和placement delete_hujing_Liu-CSDN博客
《effctive C++》p256
通常的C++程序中,new和delete表达式总是成对出现,并且分别调用了全局的operator new和operator delete,这两个全局操作符(或者直接理解为全局的函数)的基本形式如下:
void * operator new ( std::size_t) throw ( std::bad_alloc );
void operator delete (void * rawMemory) throw();
现有一个class A,语句A *p = new A;
包含几步:
- 调用operator new分配内存,operator new (sizeof(A))
- 调用构造函数生成类对象,A::A()
- 返回相应指针
现在有一个微妙的问题:如果第一步的调用成功,分配了空间,但第二布的调用却抛出异常,那如何回收分配的内存空间呢?注意,这个时候这条语句是没有成功执行的,operator new
没能成功返回指向分配的内存空间的指针,也就是说p是无效的,所以程序员没有办法回收这块内存。
庆幸的是,C++编译器会自动地调用与operator new形式相对应的operator delete来回收那块空间。
placement new
如果operator new接受的参数除了一定会有的那个size_t之外还有其他参数,则称其为placement new。由这个定义可知我们可以重载出很多不同形式的placement new,其中一个特殊的是“接受一个指针指向对象该被构造之处”的placement new,这也是placement new名字的由来:特定位置上的new,定位new。其形式如下:
void * operator new ( std::size_t, void * ptr ) throw ( );
该版本的placement new已纳入C++标准库,通过#include来引入。其用途之一是负责在vector的未使用空间上创建对象(这点在STL中详细论述)。placement new的用法如下:
A *q = new ( ptr ) A; //ptr指向对象被构造之处
同样给出带指针的placement new对应的placement delete,形式如下:
void operator delete( void* pMemory, void *ptr ) throw() ;
需要注意的有两点:
1、如果程序员自定义了placement new,那么也应定义相应形式的placement delete,否则在调用该placement new构造对象并出现异常时,编译器找不到可用的placement delete来回收空间,这将导致内存泄露!
2、placement delete只有在“伴随placement new调用而触发的构造函数”出现异常时才会被调用。针对一个指针施行的delete绝不会导致调用placement delete。即使这个指针是由placement new返回的,也不会。例如对于上文的指针q,执行语句delete q时,调用的时全局的普通的operator delete,而不是带第二个参数的operator delete。
上面说到placement new的用途之一是在vector的未使用空间上创建对象。下面参考《STL源码剖析》给出部分解释:
在创建vector时,先调用空间配置器void *allocate(size_t n)来分配内存空间(《STL源码剖析》中说明了SGI STL的空间配置器分为两级,并给出了内部实现),再调用void construct(T1 *p, const T2& value)在分配的空间上构造对象。(construct函数包含在头文件<stl_construct.h>中,T1、T2是模板参数,详见《STL源码剖析》51页)
既然是在分配的空间上构造对象,显然要用到placement new,事实正是如此,construct函数内部就调用了placement new,源码如下:
template <class T1, class T2>
inline void construct(T1* p, const T2& value){
new (p) T1(value);
}
vector的push_back函数内部则是调用construct来在末端加入新元素的。详见《STL源码剖析》122面。