当operator new无法满足某一内存分配需求时,它会抛出一个异常,以前它会返回一个null指针。在operator new抛出异常之前,它会调用客户指定的错误处理函数,即new_handler。为了指定这个用以处理内存不足的函数,客户必须调用set_new_handler,那是声明于<new>的一个标准程序库:
namespace std{
typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();
}
当operator new无法满足内存申请时,它会不断调用new_handler函数,就像下面的伪码这样:
void* operator new(std:size_t size) throw(std::bad_alloc)
{
using namespace std;
if(size = =0)
{
size = 1;
}
while(true)
{
void* result = malloc(size);
if(result != NULL)
return result;
new_handler globalHandler = set_new_handler(0);
set_new_handler(globalHandler);
if(globalHandler) (*globalHandler)();
else throw std::bad_alloc();
}
}
一个设计良好的new_handler函数必须做以下事情:
1、让更多的内存可被使用。这便造成operator new内的下一次内存分配动作可能成功。实现此策略的一个做法是,程序一开始执行就分配一大块内存,而后当new_handler第一次被调用,将它们释还给程序使用。
2、安装另一个new_handler。如果目前这个new_handler无法取得更多更多可用的内存,或许它知道另外哪个new_handler有此能力。果真如此,目前这个new_handler就可以安装另外那个new_handler以替换自己。下次当operator new调用new_handler,调用的将是最新安装的那个。
3、卸除new_handler,也就是将null指针传给set_new_handler。一旦没有安装任何new_handler,operator new会在内存分不足时抛出异常。
以上是new_handler和set_new_handler相关知识,要实现class专属之new_handler,只需令每一个class提供自己的set_new_handler和operator new即可。假设你打算处理Widget class内存分配失败情况,你的class定义看起来像是这样:
class Widget{
public:
static std::new_handler set_new_handler(sed:new_handler p) throw();
static void* operator(std::size_t size) throw(std::bad_alloc);
private:
static std::new_handler currentHandler;
};
Widget的operator new做以下事情:
1、调用标准的set_new_hander,将Widget的new_handler安装为global new_handler。
2、调用global operator new,执行实际内存之分配。如果分配失败,global operator new会调用Widget的new_handler。如果global operator new最终无法分配足够内存,会抛出一个异常。在此情况下Widget的operator new必须恢复原本的global new_handler,然后再传播异常。为确保原本的new_handler总能够被重新安装回去,Widget将global operator new视为资源并用资源管理对象防止资源泄露。
3、如果global operator new能够分配足够一个Widget对象所用内存,Widget的operator new将返回一个指针,指向分配所得。Widget的析构函数把Widget的operator new被调用前的那个global operator new恢复回来。
下面是将global operator new作为资源进行管理的资源管理类:
class NewHandlerHolder{
public:
explicit NewHandlerHolder(std::new_handler nh): handler(nh){}
~NewHandlerHolder()
{
set::set_new_handler(handler);
}
private:
std::new_handler handler;
};
这样Widget的operator new实现相当简单:
void* Widget::operator new(std::size_t size) throw(std::bad_alloc)
{
NewHandlerHolder h(std::set_new_handler(currentHandler));
return ::operator new(size);
}