写了placement new也要写placement delete

    placement new和placement delete并非C++ 兽栏中最常见的动物,如果你不熟悉它们,不要感到挫折或忧虑。回忆条款16 和17,当你写一个new表达式像这样:
widget* pw=new widget;

共有两个函数被调用:一个是用以分配内存的operator new,一个是widget的default构造函数。

       假设其中第一个函数调用成功,第二个函数却抛出异常。既然那样,步骤一的内存分配所得必须取消并恢复旧观,否则会造成 内存泄露(memory leak)。在这个时候,客户没有能力归还内存,因为如果widget构造函数抛出异常,pw尚未被赋值,客户手上也就没有指针指向该被归还的内存。取消步骤一并恢复旧观的责任因此落到C++运行期系统身上。

      如果operator new 接受的参数除了一定会有的那个size_t之外还有其他,这便是个所谓的placement new。众多placement new版本中特别有用的一个死“接受一个指针指向对象该被构造之处”,那样的operator new长相如下:

void* operator new(std::size_t  size,void* pMemory) throw(); //placement new

这个版本的new已被纳入C++标准程序库,你只要#include <new>就可以取用它。这个new的用途之一是负责在vector的未使用空间上创建对象。它同时也是最早的placement new版本。实际上它正是这个函数的命名根据:一个特定位置上的new。以上说明意味术语placement new有多重定义。当人们谈到placemnet new,大多数时候他们谈的是此一特定版本,也就是“唯一额外实参是个void*”,少数时候才是指接受任意额外实参之operator new。上下文语境往往也能够使意义不明确的话语清晰起来,但了解这一点相当重要:一般性术语“placement new”意味带任意额外参数的new,因为另一个术语“placement delete”直接派生自它。稍后我们即将遭遇后者。

    规则很简单:如果一个带额外参数的operator new没有“带相同额外参数”的对应版operaotr delete,那么当new的内存分配动作需要取消并恢复旧观时就没有任何operator delete会被调用。因此,为了消弭稍早代码中的内存泄露,类中有必要声明一个与placement new对应版本的placement delete.placement只有在“伴随placement new调用而触发的构造函数”出现异常是才会被调用。

    附带一提,由于成员函数的名称会掩盖其外围作用域中的相同名称,你必须小心避免让class 专属的new掩盖客户期望的其他news(包括正常版本)。如果你在class内声明任何operator news,它会遮掩全局的标准new形式。除非你的意思就是要阻止class的客户使用这些形式,否则请确保它们在你所生成的任何定制型operator new之外还可用。对于每一个可用的operator new也请确定对应的operator delete.如果你希望这些函数有着平常的行为,只要你的class 专属版本调用global版本即可。

 

完成以上所言的一个简单做法是,建立一个base class,内含所有正常形式的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()
	{
		::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);
	}
};


凡是想以自定形式扩充标准形式的客户,可利用继承机制及using声明式(见条款33)取得标准形式:

class widget:public StandardNewDeleteForms  //继承标准形式
{
public:
	using StandardNewDeleteForms::operator new;  //让这些形式可见
	using StandardNewDeleteForms::operator delete;

	//添加一个自定的placement new
	static void* operator new(std::size_t size,std::ostream& logSteam) throw(std::bad_alloc);
	//添加一个对应的placement delete
	static void operator delete(void* pMemory,std::ostream& logStream) throw();
};


请记住:

当你写一个placement operator new,请确定也写出了对应的placement operator delete。如果没有这样做,你的程序可能会发生隐微而时断时续的内存泄露。

当你声明placement new和placement delete,请确定不要无意识(非故意)地遮掩了它们的正常版本。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值