[原创文章欢迎转载,但请保留作者信息]
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();
而如果用的是非常规的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
//..
};