当operator new无法满足某一内存分配需求时,它就会抛出异常,但在抛出异常前,它会先调用一个客户指定的错误处理函数,一个所谓的new-handler。为了指定这个"用以处理内存不足"的函数,客户必须调用set_new_handler,那是声明与<new>的一个标准库函数:
namespace std{
typedef void (*new_handler)();
new_handler set_new_handler(new_handler p)throw();
}
new_handler是一个指向函数的指针,该函数没有返回值和参数。set_new_handler是一个获得一个new_handler并返回一个new_handler的函数,其参数是一个指针,指向operator new无法分配足够内存时该被调用的函数,其返回值指向set_new_handler 被调用前正在执行(马上要被替换 )的那个new_handler函数。可以这样使用
void outOfMem()
{
std::cerr << "Unable to satisfy request for memory\n" << std::endl;
std::abort();
}
int main()
{
std::set_new_handler(outOfMem);
int* pBigDataArry = new int[100000000L];
return 0;
}
如果上述无法为100000000个数整数分配足够的空间,outOfMem就会被调用。
一个设计良好的new_handler必须
1.让更多的内存可被使用
2.安装另一个new_handler
3.卸除new_handler 就是将null指针传给set_new_handler。一旦没有安装任何new_handler,operator new会在内存分配不成功时抛出异常
4.抛出bad_alloc(或派生自bad_alloc)异常
5.不返回,通常调用abort或exit
C++不支持class专属之new_handler。我们可以自己实现这种行为。假设处理Widget class内存分配失败的情况
class Widget{
public:
static std::new_handler set_new_handler(std::new_handler p) throw();//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;
//将获取的new_handler储存起来,返回先前的
std::new_handler Widget::set_new_handler(std::new_handler p) throw()
{
std::new_handler oldHandler = currentHandler;
currentHandler = p;
return oldHandler;
}
为了防止资源泄露,运用资源管理对象 ,这样会自动调用析构函数,以及将new_handler恢复成默认的。
class NewHandlerHolder{
public:
explicit NewHandlerHolder(std::new_handler nh)
: handler(nh){}
~NewHandlerHolder()
{
std::set_new_handler(handler);
}
private:
std::new_handler handler;
NewHandlerHolder(const NewHandlerHolder&);
NewHandlerHolder& operator=(const NewHandlerHolder&);
};
Widget在内存分配时的操作如下:
void* Widget::operator new(std::size_t size) throw(std::bad_alloc)
{
NewHandlerHolder h(std::set_new_handler(currentHandler));
return ::new_handler(size);
}
可以这样使用:
void outOfMem();
int main()
{
Widget::set_new_handler(outOfMem);
Widget* p1 = new Widget;
return 0;
}
为了能加以复用,可以写成模板的形式
template<typename T>
class NewHandlerSupport{
public:
static std::new_handler set_new_handler(std::new_handler p) throw();//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>::currentHandler = 0;
//将获取的new_handler储存起来,返回先前的
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 ::new_handler(size);
}
然后让你希望有自己专属之new-handler的类继承自该模板对类的具现化,如:
class Widget: public NewHandlerSupport<Widget>
请记住
1.set_new_handler允许客户指定一个函数,在内存分配无法获得满足时被调用
2.Nothrow new 是一个颇为局限的工具,因为它只适用于内存分配;后继的构造函数调用还是可能抛出异常。(具体内容参照书本)