Effective C++ 第八章:定制new和delete

第八章:定制new和delete

条款49:了解new-handler的行为

  • set_new_handler的参数是一个指针,指向operator new无法分配足够内存时该被调用的函数。返回值也是个指针,指向set_new_handler被调用前正在执行的那个new-handler函数。
namespace std{
	typedef void (*new_handler)();
	new_handler set_new_handler(new handler p) throw();
}

例子

void outOfMem()
{
	std::cerr << "unable to satisfy request for memory \n";
	abort();
}
int main()
{
	std::set_new_handler(outOfMem);
	int* pBig = new int[100000000L];
}

条款50:了解new和delete的合理替换时机

  1. 用来检测运用上的错误:例如在new所得内存上多次delete则会导致不确定行为。写入点在分配区块尾端之后(overruns)或写入点在分配区块起点之前(underruns)。我们可以写入签名(signature),用于查看分配的指针。
  2. 为了强化效能:处理内存碎片问题。定制版的operator new 和operator delete性能胜过缺省版本,体现在所需内存上,已经时间上。

例子:一个定制型的operation new

static const int signature = 0xDEADBEEF;
typedef unsigned char Byte;
void* operator new(std::size_t size) throw(std::bad_alloc)
{
	using namespace std;
	siez_t realSize = size + 2*sizeof(int);	//增加大小,使之能够写入两个signature
	
	void* pMem = malloc(realSize);	//调用malloc取得内存
	if(!pMem) throw bad_alloc();
	
	//将signature写入内存的最前段落和最后段落
	*(static_cast<int*>(pMem)) = signature;
	*(reinterpret_cast<int*>(static_cast<Byte*>(pMem)+realSize-sizeof(int))) = signature;
	//返回指针,指向位于第一个signature之后的内存位置
	return static_cast<Byte*> (pMem) + sizeof(int);
}
  • 齐位(alignment):许多计算机体系结构要求特定的类型必须放在特定的内存地址上。例如它可能要求指针的地址必须是4倍数或double的地址必须是8倍数。这样子会提高效率。
    • C++要求所有operator new返回指针都有适当的对齐。malloc就是在这样子的要求下工作。但自定义的new却没有。
    • Boost中的pool是一个搞笑的内存分配器。

条款51:编写new和delete时需固守常规

operator new 伪代码

void* oprator new(std::size_t size) throw(std::bad_alloc)
{
	using namespace std;
	if(size == 0){
		size = 1;
	}
	while(true){
		尝试分配size bytes;
		if(分配成功)
			return (一个指针,指向分配得来的内存);
		//分配失败;找出目的的new-handling函数
		new_handler globalHandler = set_new_handler(0);
		set_new_handler(globalHandler);
		
		if(globalHandler) *(globalHandler)();
		else throw std::bad_alloc();
	}
}

  • operator new实际上不只一次尝试分配内存,并在每次失败后调用new-handling函数。只有当指向new-handling函数的指针是null,operator new才会抛出异常。

  • operator new应该包含一个无穷循环,并在其中尝试分配内存,如果它无法满足内存需求,就应该调用new-handler.它也应该有能力处理0bytes申请。

class Base{
public:
	static void* operator new(std::size_t size) throw(std::bad_alloc);
	static void operator delete(void* rawMemory, std::size_t size) throw();
	...
}
void* Base::operator new(std::size_t size) throw(std::bad_alloc)
{
	if(size != sizeof(Base))			//如果大小错误,令标准operator new处理
		return ::operator new(size);
		
	...									//否则在这里处理
}
  • operator delete应该在收到null指针时不做任何事情。
void Base::operator delete(void* rawMemory, std::size_t size) throw()
{
	if(rawMemory == 0)	return;		//null指针,什么都不做
	if(size!=sizeof(Base)){
		::operator delete(rawMemory);	//如果大小错误,令标准版operator delete处理
		return ;
	}
	...	//归还rawMemory内存
	return;
}
  • 小结:遇到无法处理的情况,交给原生的new 和 delete处理。

条款52:写了placement new 也要写placement delete

Widget* pw = new Widget;

共有两个函数被调用,一个是用以分配内存的operator new,一个是widget的default构造函数。
如果第一个函数调用成功,但是第二个函数调用失败。那么必须回收分配出去的内存,否则会造成内存泄漏。这一工作由C++运行期系统身上。

运行期系统会调用步骤一所调用的operator new函数对应的operator delete版本。如果有多个版本的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()
	{ return 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); }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值