正文

条款7:预先准备好内存不够的情况2008-10-15 19:25:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/lqf110/38921.html

分享到:

采用new分配内存,必须对内存分配失败时做好准备。

在内存分配之前,我们可以预先指定一个出错函数:

typedef void (*new_handle)()

set_new_handler(new_handler p) throw();

可以看到,new_handler是一个自定义的函数指针类型,它指向一个没有输入参数也没有返回值的函数。set_new_handler的输入参数是operator new分配内存失败时要调用的出错处理函数的指针,返回值是set_new_handler没调用之前就已经在起作用的旧的出错处理函数的指针。

可以象下面这样使用set_new_handler:

// function to call if operator new can't allocate enough memory
void nomorememory()
{
	cerr << "unable to satisfy request for memory\n";
	abort();
}

int main() { set_new_handler(nomorememory); int *pbigdataarray = new int[100000000]; ... }
我们可以对每个类重载new,并且提供set_new_handler函数。以下是这种形式:
class x {
public:
	static new_handler set_new_handler(new_handler p);
	static void * operator new(size_t size);
	
private:
	static new_handler currenthandler;
};
new_handler x::set_new_handler(new_handler p)
{
	new_handler oldhandler = currenthandler;
	currenthandler = p;
	return oldhandler;
}
void * x::operator new(size_t size)
{
         new_handler globalhandler =	std::set_new_handler(currenthandler);
		
         void *memory;
	// 尝试分配内存

	try{
 		memory = ::operator new(size);
	}
	// 恢复旧的new_handler
	catch (std::bad_alloc&) {
		std::set_new_handler(globalhandler);      
		throw;	// 抛出异常
	}
	std::set_new_handler(globalhandler);	// 恢复旧的new_handler
	return memory;
}
 

你会注意到,处理以上类似情况,如果不考虑类的话,实现代码是一样的,这就很自然地想到在别的地方也能重用它们。正如条款41所说明的,继承和模板可以用来设计可重用代码。在这里,我们把两种方法结合起来使用,从而满足了你的要求。

你只要创建一个“混合风格”(mixin-style)的基类,这种基类允许子类继承它某一特定的功能——这里指的是建立一个类的new-handler的功能。之所以设计一个基类,是为了让所有的子类可以继承set_new_handler和operator new功能,而设计模板是为了使每个子类有不同的currenthandler数据成员。这听起来很复杂,不过你会看到代码其实很熟悉。区别只不过是它现在可以被任何类重用了。

template<class t>	// 提供类set_new_handler支持的
class newhandlersupport {	// 混合风格”的基类
public:
	static new_handler set_new_handler(new_handler p);
	static void * operator new(size_t size);

private:
	static new_handler currenthandler;
};

template<class t>
new_handler newhandlersupport<t>::set_new_handler(new_handler p)
{
	new_handler oldhandler = currenthandler;
	currenthandler = p;
	return oldhandler;
}

template<class t>
void * newhandlersupport<t>::operator new(size_t size)
{
	new_handler globalhandler =
		std::set_new_handler(currenthandler);
	void *memory;
	try {
		memory = ::operator new(size);
	}
	catch (std::bad_alloc&) {
		std::set_new_handler(globalhandler);
		throw;
	}
	
	std::set_new_handler(globalhandler);
	return memory;
}
// this sets each currenthandler to 0

template<class t>
new_handler newhandlersupport<t>::currenthandler;

有了这个模板类,对类x加上set_new_handler功能就很简单了:只要让x从newhandlersupport<x>继承: // note inheritance from mixin base class template. (see // my article on counting objects for information on why // private inheritance might be preferable here.) class x: public newhandlersupport<x> { ... // as before, but no declarations for }; // set_new_handler or operator new

1993年前,c++一直要求在内存分配失败时operator new要返回0,现在则是要求operator new抛出std::bad_alloc异常。很多c++程序是在编译器开始支持新规范前写的。c++标准委员会不想放弃那些已有的遵循返回0规范的代码,所以他们提供了另外形式的operator new(以及operator new[]——见条款8)以继续提供返回0功能。这些形式被称为“无抛出”,因为他们没用过一个throw,而是在使用new的入口点采用了nothrow对象:

class widget { ... };

widget *pw1 = new widget;// 分配失败抛出std::bad_alloc if

if (pw1 == 0) ...	// 这个检查一定失败
widget *pw2 = new (nothrow) widget; 	// 若分配失败返回0
	
if (pw2 == 0) ...	// 这个检查可能会成功

不管是用“正规”(即抛出异常)形式的new还是“无抛出”形式的new,重要的是你必须为内存分配失败做好准备。最简单的方法是使用set_new_handler,因为它对两种形式都有用。

阅读(2224) | 评论(0)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

暂无评论
您需要登录后才能评论,请 登录 或者 注册