“placement new”一般是专指指定了位置的new(std::size_t size, void *mem)
,用于vector
申请capacity
剩余的可用内存。 但广义的”placement new”指的是拥有额外参数的operator new
。html
new
和delete
是要成对的,由于当构造函数抛出异常时用户没法获得对象指针,于是delete
的责任在于C++运行时。 运行时须要找到匹配的delete
并进行调用。所以当咱们编写了”placement new”时,也应当编写对应的”placement delete”, 不然会引发内存泄露。在编写自定义new
和delete
时,还要避免不当心隐藏它们的正常版本。函数
当构造函数抛出异常时,C++会调用与new
一样签名的delete
来撤销new
。但若是咱们没有声明对应的delete
:spa
class Widget{ public: static void* operator new(std::size_t size, std::ostream& log) throw(std::bad_alloc); Widget(){ throw 1; } }; Widget *p = new(std::cerr) Widget;
构造函数抛出了异常,C++运行时尝试调用delete(void *mem, std::ostream& log)
, 但Widget
没有提供这样的delete
,因而C++不会调用任何delete
,这将致使内存泄露。 因此在Widget
中须要声明一样签名的delete
:指针
static void operator delete(void *mem, std::ostream& log);
但客户还可能直接调用delete p
,这时C++运行时不会把它解释为”placement delete”,这样的调用会使得Widget
抛出异常。 因此在Widget
中不只要声明”placement delete”,还要声明一个正常的delete
。code
class Widget{ public: static void* operator new(std::size_t size, std::ostream& log) throw(std::bad_alloc); static void operator delete(void *mem, std::ostream& log); static void operator delete(void *mem) throw(); Widget(){ throw 1; } };
这样,不管是构造函数抛出异常,仍是用户直接调用delete p
,内存都能正确地回收了。orm
在Item 33中提到,类中的名称会隐藏外部的名称,子类的名称会隐藏父类的名称。 因此当你声明一个”placement new”时:htm
class Base{ public: static void* operator new(std::size_t size, std::ostream& log) throw(std::bad_alloc); }; Base *p = new Base; // Error! Base *p = new (std::cerr) Base; // OK
普通的new
将会抛出异常,由于”placement new”隐藏了外部的”normal new”。一样地,当你继承时:对象
class Derived: public Base{ public: static void* operator new(std::size_t size) throw(std::bad_alloc); }; Derived *p = new (std::clog) Derived; // Error! Derived *p = new Derived; // OK
这是由于子类中的”normal new”隐藏了父类中的”placement new”,虽然它们的函数签名不一样。 但Item 33中提到,按照C++的名称隐藏规则会隐藏全部同名(name)的东西,和签名无关。继承
为了不全局的”new”被隐藏,先来了解一下C++提供的三种全局”new”:内存
void* operator new(std::size_t) throw(std::bad_alloc); // normal new void* operator new(std::size_t, void*) throw(); // placement new void* operator new(std::size_t, const std::nothrow_t&) throw(); // 见 Item 49
为了不隐藏这些全局”new”,你在建立自定义的”new”时,也分别声明这些签名的”new”并调用全局的版本。 为了方便,咱们能够为这些全局版本的调用声明一个父类StandardNewDeleteForms
:
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); } };
而后在用户类型Widget
中using StandardNewDeleteForms::new/delete
便可使得这些函数均可见:
class Widget: public StandardNewDeleteForms { // inherit std forms public: using StandardNewDeleteForms::operator new; using StandardNewDeleteForms::operator delete; static void* operator new(std::size_t size, std::ostream& log) throw(std::bad_alloc); // 自定义 placement new static void operator delete(void *pMemory, std::ostream& logStream) throw(); // 对应的 placement delete };