当operator new 无法满足某一内存分配需求时,它会抛出异常,以前它会返回null指针.
当operator new 抛出异常反映一个未获满足的内存需求之前,它会先调用一个客户指定的错误处理函数,一个所谓的new-handler。
namespace std
{
typedef void(*new_handler)();
new_handler set_new_handler(new_handler p) throw();//注意C++11应该是使用noexcept 表明不会抛出异常
}
set_new_handler
的参数是个指针,指向operatornew无法分配足够内存时该调用的函数,其返回值也是个指针,指向set_new_handler
被调用前正在执行的那个new_handler
函数.
可以这样使用set_new_handler
:
void outOfMem()
{
std::cerr << "Unable to satisfy request for memory\n";
std::abort();
}
int main()
{
std::set_new_handler(outOfMem);
int * p = new int[10000000000];
...
}
设计一个良好的new-handler函数必须做以下事情:
1.让更多内存可被使用,这样可以让operatornew内的下一次分配动作可能成功,实现这个策略的一个做法是:程序一开始就分配一大块内存,当newhandler第一次被调用,将它们释还给程序使用
2.安装另一个new-handler
,如果目前这个handler
无法取得更多可用内存,或许它知道另外哪个handler
有此能力.果真如此,目前这个newhandler
就可以安装另外那个newhandler
以替换自己.下次当operator new
调用new-handler
调用的是最新安装的那个
3.卸除new-handler:也就是将null指针传给set_new_handler.一旦没有安装任何new-handler,operator new会在内存分配不成功时抛出异常.
4.抛出bad_alloc的异常
5.不返回,通常调用abort或exit
有时候,或许你希望以不同的方式处理内存分配失败情况,你希望视被分配物属于哪个class而定,但C++并不支持类专属new-handler
,但你可以自己实现出这种行为,只需令每一个class提供自己的set_new_handler
和operator new
即可.
其中set_new_handler
使客户得以指定类专属的new-handler,operator new则确保在分配class对象内存的过程中以类专属的new-handler替换全局new handler.
假设你打算处理widget class的内存分配失败情况.首先你必须登录当operator new无法为一个widget对象分配足够内存时调用的函数,看起来像这样:
class widget
{
public:
static std::new_handler set_new_handler(std::new_handler p) noexcept;
static void* operator new(std::size_t size)thorw(std::bad_alloc);
private:
static std::new_handler currentHandler;
};
std::new_handler widget::currentHandler = 0;
widget内的set_new_handler函数会将它获得的指针存储起来,然后返回先前存储的指针:
std::new_handler Widget::set_new_handler(std::new_handler p) noexcept
{
std::new_handler oldHandler = currentHandler;
currentHandler = p;
return 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 new-handler…如果global operator new最终无法分配内存,抛出bad_alloc异常.
3.如果global operator new能够分配足够一个wdiget对象所用的内存,widget的operator new会返回一个指针,指向分配所得.widget析构函数会管理global new_handler,它会自动将widget’s operator new 被调用前的那个global new-handler恢复回来
class NewHandlerHolder {
public:
explicit NewHandlerHolder(std::new_handler nh)://取得目前new-handler
handler(nh){}
NewHandlerHolder() { //释放它
std::set_new_handler(handler);
}
private:
std::new_handler handler;//记录下来
NewHandlerHolder(const NewHandlerHolder&);//阻止copying
NewHandlerHolder& operator=(const NewHandlerHolder&);
};
//对类Widget内的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); //分配内存或抛出异常。恢复global new-handler
}
于是Wiget的客户可以如下使用其new-handling:
void outOfMem();//函数声明。此函数在Widget对象分配失败时被调用
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 0表明关闭outOfMem函数的调用
Widget* pw2 = new Widget;//如果内存分配失败,立刻抛出异常。
总结:
set_new_handler允许客户指定一个函数,在内存分配无法获得满足时调用.