正文

条款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,因为它对两种形式都有用。

阅读(5430) | 评论(0)


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

评论

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