C++进阶_Effective_C++第三版(八) 定制new和delete Customizing new and delete

定制new和delete

Customizing new and delete
多线程环境下的内存管理,比单线程系统更加复制,由于heap是一个可被改动的全局性资源,因此多线程系统访问这一类资源的竞速状态出现机会。使用无锁算法或精心防止并发访问时,调用内存可能很容易导致管理heap的数据结构内容出错。

49、了解new-handler的行为

Understand the behavior of the new-handler.
当operator new无法满足某一内存分配需求时,它会派出异常,以前会返回一个null指针。新式的编译器支持,在未获满足的内存需求之前,先调用一个客户指定的错误处理函数,一个所谓的new-handler。为了指定这个“用以处理内存不足”的函数,客户必须调用set_new_handler,那是声明于的一个标准程序库函数:

namespace std {
	typedef void (*new_handler) {	};
	new_handler set_new_handler(new_handler p) throw();
}

new_handler是个typedef,定义出一个指针指向函数,该函数没有参数也不返回任何东西。set_new_handler则是“获得一个new_handler并返回一个new_handler的函数”。set_new_handler声明式尾端“throw()”是一份异常明细,表示该函数不抛出任何异常。set_new_handler的参数是个指针,指向operator new无法分配足够内存时候被调用的函数。其返回值也是个指针,指向set_new_handler被调用前正在执行的那个new_handler函数。可以如下使用:

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

上述程序申请空间失败后调用outOfMem,打印输出后结束程序。
new_handler函数设计的几点要求:让更多的内存可被使用,安装另一个new_handler,卸除new_handler(将null指针传给set_new_handler)。不返回,通常调用abort或exit。
C++的operator new在旧版本无法分配足够内存时返回null,新版本的是抛出bad_alloc异常,考虑到兼容性问题,提供另一种机制实现分配失败返回null。被称为nothrow形式:

class Widget{		};
Widget* pw1 = new Widget; 			//分配失败抛出bad_alloc
if(pw1 == 0)//这个测试一定失败
Widget* pw2 = new (std::nothrow) Widget; //如果分配失败,返回0
if(pw2 == 0)//这个测试可能成功

nothrow new对异常的强制保证性并不高,因为在表达式中发生两件事,第一nothrow版的operator new被调用,用以分配足够内存给Widget对象。如果分配失败便返回null指针,第二如果分配成功,Widget构造函数会被调用,此时Widget构造函数内如果有new一些内存,无法强迫它再次使用nothrow new。

  • set_new_handler允许客户指定一个函数,在内存分配无法获得满足时被调用。
  • Nothrow new是一个颇为局限的工具,因为它只适用于内存分配,后继的构造函数调用还是可能抛出异常。

50、了解new和delete的合理替换时机

Understand when it makes sense to replace new and delete.
写一个定制型operator new和operator delete如下:

static const int signature = 0xDEADBEEF;
typedef unsigned char Byte;
void* operator new(std::size_t size) throw(std::bad_alloc)
{
	using namespace std;
	size_t realSize = size+2*sizeof(int);//增加大小,使能够塞入两个signature
	void *pMem = malloc(realSize);
	if(!pMem) throw bad_alloc();
	//将signature写入内存的最前段落和最后段落
	*( static_cast< int *>(pMem)) = signature;
	*(reinterpret_cast<int *>( static_cast< Byte *>(pMem)+realSize-sizeof(int))) = signature;
	return static_cast< Byte *>(pMem) + sizeof(int);
}

替换编译器提供的operator new和operator delete理由:
用来检测运用上的错误:如果将new所得内存delete掉却不幸失败,会导致内存泄漏,如果new所得内存身上多次delete则会导致不确定行为等。
为了强化效能:编译器版本所带的operator new和operator delete主要用于一般目的,不但可被长时间执行的程序接受,也可被执行时间少于一秒的程序接受,而且还包括大块内存,小块内存,大小混合型内存等等,编译器版本采取的是中庸之道,对于每个需求都是适度的好。
为了收集使用上的统计数据:在使用分配内存之前,应该先收集软件如何使用其动态内存。分配区块的大小如何分布,使用寿命,哪种次序来分配和归还等。
为了增加分配和归还的速度:泛用性分配器往往比定制型分配器慢,特别是当定制型分配器专门针对某特定类型的对象而设计时。
为了降低缺省内存管理器带来的空间额外开销:泛用性内存管理器往往不只比定制型慢,它们往往还使用更多内存,因为它们常常在每个分配区身上加一些额外开销。
为了弥补缺省分配器中的非最佳对齐:在x86体系结构上都是8-byte对齐的化doubles的访问最是快速。
为了将相关对象成簇集中:如果知道特定的某个数据结构往往被一起使用,且希望处理这些数据时将内存页错误的频率降至最低。这样可以将它们成簇集中在尽可能少的内存页上。
为了获得非传统的行为:如可能会希望分配和归还共享内存内的区块。

  • 有许多理由需要写个自定的new和delete,包括改善效能、对heap运用错误进行调试、收集heap使用信息。

51、编写new和delete时需固守常规

Adhere to convention when writing new and delete.
对于定制型operator new和operator delete编写要遵守一些规则,最好的实现方式就是保持和原始的new和delete保持一致。
比如对于new,应该提供如果内存不足时必须调用new-handling函数,必须对零内存需求处理:

void* operator 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 throw(std::bad_alloc);
	}
}
void operator delete(void *rawMemory) throw()
{
	if(rawMemory == 0) return;
	//归还rawMemory所指的内存
}
  • operator
    new应该内含一个无穷循环,并在其中尝试分配内存,如果它无法满足内存需求,就该调用new-handler。它也应该有能力处理0bytes申请。Class专属版本则还应该处理比正确大小更大的错误申请。
  • operator delete应该在收到null指针时不做任何事。Class专属版本则还应该处理比正确大小更大的错误申请。

上一篇: C++进阶_Effective_C++第三版(七) 模板与泛型编程 Templates and Generic Programming
下一篇: C++进阶_Effective_C++第三版(一) 让自己习惯C++ Accustoming Yourself to C++

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风华一叶知秋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值