读书笔记:Effective C++ 炒冷饭 - Item52 写了placement new也要写placement delete
[原创文章欢迎转载,但请保留作者信息]
Justin 于 2010-06-15
在item16和17里大师曾经介绍过,一个new的过程大致分两步:
- 申请内存
- 调用构造函数构造新对象
如果第一步成功而第二步失败,一个成熟的系统应该能回撤第一步,释放刚分配的内存空间。
如果第一步用的是普通的new函数,C++是能够找到与之匹配的delete函数的。
而如果用的是非常规的new函数,情况就不一样了。这里说的“非常规new”,指的是placement new。
这里说的placement new指的是除去size_t参数外,还包含了其他的参数的new函数。
最原始的placement new版本在C++的new库中:
比较常见的用途是申请一大批内存放在某个vector中,到需要分配内存的时候用该placement new指定在某个地址开始申请内存。
在调用placement new的过程中,一旦第二步出错,系统依照惯例会用一个delete来释放内存,不过因为这次new的方式不同寻常,也就需要一个不同寻常的delete来释放。
系统需要的是一个参数类型、个数都一致的delete,placement delete来释放由那个placement new鼓捣出来的内存空间。
如果找不到……对不起,内存泄漏。
但是有一点需要注意的是:如果使用了placement new,除了要写好与之对应的placement delete外,还要再写一个“常规”的delete函数。
因为对于delete而言,placement delete是在当placement new遇见失败异常时释放新近分配的内存用的;“常规”的delete则是用于正常途径的delete。如下的语句,是不会调用placement delete的。
最后要提的一点是:注意不同名字域中的名字覆盖/隐藏。
如果在某个类中只声明了一个placement new,那么用户就无法使用全局范围的默认new函数;如果子类中只声明了一个placement new,那么父类的另外形式的new函数就会被隐藏。如何做到透明的使用所有可能的new/delete?
直接粘贴大师的代码吧,一来是这个已经很直接没有必要再另做消化,二来我感冒了……
Justin 于 2010-06-15
在item16和17里大师曾经介绍过,一个new的过程大致分两步:
- 申请内存
- 调用构造函数构造新对象
如果第一步成功而第二步失败,一个成熟的系统应该能回撤第一步,释放刚分配的内存空间。
如果第一步用的是普通的new函数,C++是能够找到与之匹配的delete函数的。
//
normal form of new operator
void * operator new (std::size_t) throw (std::bad_alloc);
// is matched with the global one
void operator delete( void * rawMemory) throw ();
// or the class-scope one
void operator delete( void * rawMemory, std::size_t size) throw ();
void * operator new (std::size_t) throw (std::bad_alloc);
// is matched with the global one
void operator delete( void * rawMemory) throw ();
// or the class-scope one
void operator delete( void * rawMemory, std::size_t size) throw ();
而如果用的是非常规的new函数,情况就不一样了。这里说的“非常规new”,指的是placement new。
这里说的placement new指的是除去size_t参数外,还包含了其他的参数的new函数。
最原始的placement new版本在C++的new库中:
void
*
operator
new
(std::size_t,
void
*
pMemory)
throw
();
比较常见的用途是申请一大批内存放在某个vector中,到需要分配内存的时候用该placement new指定在某个地址开始申请内存。
在调用placement new的过程中,一旦第二步出错,系统依照惯例会用一个delete来释放内存,不过因为这次new的方式不同寻常,也就需要一个不同寻常的delete来释放。
系统需要的是一个参数类型、个数都一致的delete,placement delete来释放由那个placement new鼓捣出来的内存空间。
如果找不到……对不起,内存泄漏。
但是有一点需要注意的是:如果使用了placement new,除了要写好与之对应的placement delete外,还要再写一个“常规”的delete函数。
因为对于delete而言,placement delete是在当placement new遇见失败异常时释放新近分配的内存用的;“常规”的delete则是用于正常途径的delete。如下的语句,是不会调用placement delete的。
delete aObject;
最后要提的一点是:注意不同名字域中的名字覆盖/隐藏。
如果在某个类中只声明了一个placement new,那么用户就无法使用全局范围的默认new函数;如果子类中只声明了一个placement new,那么父类的另外形式的new函数就会被隐藏。如何做到透明的使用所有可能的new/delete?
直接粘贴大师的代码吧,一来是这个已经很直接没有必要再另做消化,二来我感冒了……
class
StandardNewDeleteForms {
public :
// normal new/delete
static void * operator new (std::size_t size) throw (std::bad_alloc)
{ return :: operator new (size); }
static void operator delete( void * pMemory) throw ()
{ :: operator delete(pMemory); }
// placement new/delete
static void * operator new (std::size_t size, void * ptr) throw ()
{ return :: operator new (size, ptr); }
static void operator delete( void * pMemory, void * ptr) throw ()
{ return :: operator delete(pMemory, ptr); }
// nothrow new/delete
static void * operator new (std::size_t size, const std::nothrow_t & nt) throw ()
{ return :: operator new (size, nt); }
static void operator delete( void * pMemory, const std::nothrow_t & ) throw ()
{ :: operator delete(pMemory); }
};
class Widget: public StandardNewDeleteForms { // inherit std forms
public :
using StandardNewDeleteForms:: operator new ; // make those
using StandardNewDeleteForms:: operator delete; // forms visible
static void * operator new (std::size_t size, // add a custom
std::ostream & logStream) // placement new
throw (std::bad_alloc);
static void operator delete( void * pMemory, // add the corres-
std::ostream & logStream) // ponding place-
throw (); // ment delete
// ..
};
public :
// normal new/delete
static void * operator new (std::size_t size) throw (std::bad_alloc)
{ return :: operator new (size); }
static void operator delete( void * pMemory) throw ()
{ :: operator delete(pMemory); }
// placement new/delete
static void * operator new (std::size_t size, void * ptr) throw ()
{ return :: operator new (size, ptr); }
static void operator delete( void * pMemory, void * ptr) throw ()
{ return :: operator delete(pMemory, ptr); }
// nothrow new/delete
static void * operator new (std::size_t size, const std::nothrow_t & nt) throw ()
{ return :: operator new (size, nt); }
static void operator delete( void * pMemory, const std::nothrow_t & ) throw ()
{ :: operator delete(pMemory); }
};
class Widget: public StandardNewDeleteForms { // inherit std forms
public :
using StandardNewDeleteForms:: operator new ; // make those
using StandardNewDeleteForms:: operator delete; // forms visible
static void * operator new (std::size_t size, // add a custom
std::ostream & logStream) // placement new
throw (std::bad_alloc);
static void operator delete( void * pMemory, // add the corres-
std::ostream & logStream) // ponding place-
throw (); // ment delete
// ..
};