条款49中关于new-handler行为
来源:互联网 发布:域名到期如何续费 编辑:程序博客网 时间:2024/04/30 01:21
当operator new无法满足某一内存分配时,就会抛出一次。以前它会返回NULL指针,现在某些旧式编译器也还这么做。
namespace std{ typedef void(*new_handler)(); new_handler set_new_handler(new_handler p) throw(); }
new_handler是个函数指针,该函数没有参数也不返回任何东西。set_new_handler是设置一个new_handler并返回一个new_handler函数,返回的new_handler是指向set_new_handler被调用前正在执行的那个new-handler函数。后面的throw是一份异常明细,表示该函数不抛出异常。可以这样使用
#include <iostream>using namespace std;void outOfMem(){std::cerr << "Unable to satisfy request for memory\n";std::abort();}int main(){std::set_new_handler(outOfMem);int *pBigDataArray1 = new int[500000000L];int *pBigDataArray2 = new int[500000000L];int *pBigDataArray3 = new int[500000000L];int *pBigDataArray4 = new int[500000000L];return 0;}设计良好的new-handler必须做好以下事情:
- 让更多内存可被使用。这样可以造成operator new内的下一次内存分配动作可能成功。一个做法是,程序一开始就分配一大块内存,当new-handler第一次被调用时将它释放。
- 安装另一个new-handler。当前的new-handler无法取得更多内存时,或许它直到哪个new-handler有此能力。
- 卸除new-handler。即将null指针传给set_new_handler,一旦没有安装任何new-handler,operator new在内存分配不成功时便抛出异常。
- 抛出bad_alloc(或派生自bad_alloc)的异常。这样的异常不会被operator new捕捉,因此不会被传播到内存索求处。
- 不返回。通常abort或exit。
有时候,我们希望处理内存分配失败的情况和class相关。例如
class X{ public: static void outOfMemory(); …… }; class Y{ public: static void outOfMemory(); …… }; X* p1=new X;//分配不成功,调用X::outOfMemory Y* p2=new Y;//分配不成功,调用Y::outOfMemory
C++并不支持class专属的new-handler,但是我们自己可以实现这种行为。令每一个class提供自己的set_new_handler和operator new即可。……………………………………………………………………………………
现在打算处理Widget class内存分配失败的情况。首先要有一个operator new无法为Widget分配足够内存时的调用函数,即new_handler函数
class Widget{ public: static std::new_handler set_new_handler(std::new_handler p) throw(); static void* operator new(std::size_t size) throw(std::bad_alloc); private: static std::new_handler currentHandler; }; std::new_handler Widget::currentHandler=0; std::new_handler Widget::set_new_handler(std::new_handler p) throw() { std::new_handler oldHandler=currentHandler; currentHandler=p; reutrn oldHandler; }
Widget的operator new做以下事情:
1、调用标准set_new_handler,告知Widget错误处理函数。这会将Widget的new-handler安装为global new-handler。
2、调用global operator new,如果失败,global operator new会调用Widget的new-handler,因为第一步。如果global operator new最终无法分配足够内存,会抛出一个bad_alloc异常。这时Widget的operator new要恢复原本的global new-handler,之后在传播异常。
3、如果global operator new调用成功,Widget的operator new会返回一个指针,指向分配的内存。Widget析构函数会管理global new-handler,它会将Widget’s operator new被调用前的那个global new-handler恢复回来。
class NewHandlerHolder{ public: explicit NewHandlerHolder(std::new_handler nh) :handlere(nh){} ~NewHandlerHolder() { std::set_new_handler(handler); } private: std::new_handler handler; NewHandlerHolder&(const NewHandlerHolder&);//防止copying NewHandlerHolder& operator-(const NewHandlerHolder&); };
这使得Widget’s operator new的实现变得简单
void* Widget::operator new(std::size_t size) throw(std::bad_alloc) { NewHandlerHolder h(std::set_new_handler(currentHandler));//安装Widget的new-handler return ::operator new(size); }
Widget客户应该类似这样使用其new-handling
void outOfMem(); Widget::set_new_handler(outOfMem);//设定outOfmem为Widget的new-handling函数 Widget* pw1=new Widget;//内存分配失败,则调用outOfMEM std::string* ps=new std::string;//内存分配失败则调用global new-handling(如果有) Widget::set_new_handler(0);//设定Widget专属new-handling为null Widget* pw2=new Widget;//内存分配失败则立刻抛出异常
实现这个方案的class代码基本相同,用个基类base class加以复用是个好的方法。可以用个template base class,如此以来每个derived class将获得实体互异的class data复件。这个base class让其derived class继承它获取set_new_handler和operator new,template部分确保每一个derived class获得一个实体互异的currentHandler成员变量。
template<typename T> class NewHandlerSupport{ public: static std::new_handler set_new_handler(std::new_handler p) throw(); static void* operator new(std::size_t size) throw(std::bad_alloc); …… private: static std::new_handler currentHandler; }; template<typename T> std::new_handler NewHandlerSupport<T>::set_new_handler(std::new_handler p) throw() { std::new_handler oldHandler=currentHandler; currentHandler=p; return oldHandler; } template<typename T> void* NewHandlerSupport<T>::operator new(std::size_t size) throw(std::bad_alloc) { NewHandlerHolder h(std::set_new_handler(currentHandler); return ::operator new(size); } //将每一个currentHandler初始化为null template<typename T> std::new_handler NewHandlerSupport<T>::currentHandler=0;有了这个class template,为Widget添加set_new_handler就容易了 class Widget:public NewHandlerSupport<Widget>{ …… };
在template base class中,从未使用类型T。因为currentHandler是static类型,使用模板的话会是每个class都有自己的currentHandler。如果使用多重继承,要注意**条款**40所提到的内容。
C++中operator new分配失败抛出异常bad_alloc,但是旧标准是返回null指针。旧标准这个形式为nothrow形式。
class Widget{}; Widget* pw1=new Widget;//分配失败,抛出bad_alloc if(pw1==null)//判断是否分配成功。但是这个测试失败 Widget* pw2=new(std::nothrow)Widget;//分配失败,返回null if(pw2==null)//可以侦测
new(std::nothrow) Widget发生两件事,第一分配内存给Widget对象,如果失败返回null指针。第二,如果成功,调用Widget的构造函数,但是在这个构造函数做什么,nothrow new并不知情,有可能再次开辟内存。如果在构造函数使用operator new开辟内存,那么还是有可能抛出异常并传播。使用nothrow new只能保证operator new不抛出异常,不能保证像new(std::nothrow) Widget这样的表达式不抛出异常。所以,并没有运用nothrow的需要。
总结
- set_new_handler允许客户指定一个函数,在内存分配无法获得满足时被调用。
- nothrow new是一个颇为局限的工具,因为它只适用于内存分配;后继的构造函数调用还是有可能抛出异常。
- 条款49中关于new-handler行为
- 条款49:了解new-handler的行为
- 条款49:了解new handler的行为
- 条款49:了解new-handler的行为
- 条款49:了解new-handler的行为
- 条款49 了解new-handler的行为
- effective C++ 条款 49:了解new-handler的行为
- Effective C++ 条款49:了解new-handler的行为
- Effective C++ 条款 49:了解new-handler的行为
- 《Effective C++》:条款49:了解new-handler的行为
- Effective C++ — 条款49:了解new-handler的行为
- Effective C++ 3e----new & delete(八)条款49:了解new-handler的行为
- Item 49:new handler的行为
- 了解new-handler的行为
- 关于new-handler
- 关于new Handler().postDelayed()
- 关于new Handler().postDelayed()
- 关于new Handler().postDelayed()
- RESTful WebService入门
- 【工具优化】Windows版Vim的优化设置
- Navicat Premium 11 数据库图形工具
- BitmapFactory.Options 解决加载大图片OOM
- UI基础2控件:UILabel,UIImageView
- 条款49中关于new-handler行为
- Matlab 形态学常用算法
- Object-C学习比较费劲的3点原因
- svn as git
- android 中Handler 引起的内存泄露
- 在JQuery的validate功能中使用remote实现Ajax功能后台验证
- docker的使用
- ANDROID BASE64编码解码
- NSURLSession下载文件—断点续传