了解new-handler的行为
本节条款讲述的技术是,在operator new抛出异常以前,会先调用一个客户指定的错误处理函数:new-handler。当内存分配失败的时候如何自定义并使用这个内存异常处理函数。关键语句就是set_new_handler
。作者重点强调如何对于class的内存分配,实现不同的内存分配错误处理函数。
我们先来举个书上的例子,先弄明白如何使用new-handler技术。
如下代码:
void outOfMem()
{
std::cerr<<"Unable to satisfy request for memoryn";
std::abort();//强制终止所在进程
}
int main()
{
std::set_new_handler(outOfMem);
int *pBigDataArray=new int[100000000L];
return 0;
}
如上代码,因为调用了set_new_handler(outOfMem);
,如果内存分配失败,则会转到outOfMen函数继续执行。这样我们就萌发出对于我们自己定义的不同类,能不能实现不同类的内存分配问题用不同的函数进行处理的想法。比如说类A有类A的自定义内存处理函数,类B有类B的自定义内存处理函数,为了实现这种功能,作者提出了以下实现方法。
不过顺带提一下,我们定义的内存处理函数要有以下特点:
1.让更多内存可被使用。这样可以造成operator new内的下一次内存分配动作可能成功。一个做法是,程序一开始就分配一大块内存,当new-handler第一次被调用时将它释放。
2.安装另一个new-handler。当前的new-handler无法取得更多内存时,或许它直到哪个new-handler有此能力。
3.卸除new-handler。即将null指针传给set_new_handler,一旦没有安装任何new-handler,operator new在内存分配不成功时便抛出异常。
4.抛出bad_alloc(或派生自bad_alloc)的异常。这样的异常不会被operator new捕捉,因此不会被传播到内存索求处。
5.不返回。通常abort或exit。
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&);
};
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;
}
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);
}
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 NewHandlerHolder
,是为了保存替换前的内存处理函数,我们来跟踪一下以上代码的执行过程。首先,声明一个outOfMem()
,接着通过Widget::set_new_handler(outOfMem)
语句设置Widget
内的static std::new_handler currentHandler
变量。然后,调用new Widget
时,我们进入Widget::operator new(std::size_t size) throw(std::bad_alloc)
函数体内,NewHandlerHolder h(std::set_new_handler(currentHandler));
语句起到的作用如下,1.在定义一个NewHandlerHolder
对象的时候将替换前的内存处理函数保存在该对象的std::new_handler handler
变量中,2.设置新的针对类Widget的new_handler函数。执行::operator new(size)
进行内存分配,如果内存分配出错则调用刚替换的内存处理函数。在退出该函数体的时候,因为NewHandlerHolder
的析构函数的,还原内存处理函数,不影响以后别的内存分配异常的执行。
以上版本实现了我们的需求功能。
然而,作者又提出了另一种利用模板实现的方法:
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&);
};
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);
template<typename T>
std::new_handler NewHandlerSupport<T>::currentHandler=0;
class Widget:public NewHandlerSupport<Widget>{
};
这种实现方式对我来说,暂时感觉别扭,可是它却体现出模板潜在的编程价值。当我们调用语句class Widget:public NewHandlerSupport<Widget>
时,我们实例化了一个NewHandlerSupport<Widget>
类,我们的类Widget
继承自该类,所以widget类中继承了NewHandlerSupport的函数,实际程序执行过程和第一种方式一样,唯一不同的地方就是类widget不用重新定义有关内存处理的静态函数。