考虑如下代码:
void outOfMem() {
std::cerr << "unable to satisfy request for memory\n";
std::abort();
}
int main() {
std::set_new_handler(outOfMem);
int *p = new int[1000000000000000]; //异常
return 0;
}
当operator new
无法满足内存申请时,它会不断调用new-handler
函数,直到找到足够的内存。
设计一个良好的new-handler
函数必须做以下事情
- 让更多内存可被使用
- 安装另外一个
new-handler
- 卸除
new-handler
- 抛出
bad_alloc
- 不返回
有时你想以不同的方式处理内存分配失败的情况:
class X {
public:
static void outOfMemory();
};
class Y {
public:
static void outOfMemory();
};
X *p1 = new X; // 如果分配不成功,调用X::outOfMemory
Y *p1 = new Y; // 如果分配不成功,调用Y::outOfMemory
解决办法是令每个class
提供自己的set_new_hanlder
和operator new
:
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(new_handler p) throw()
{
std::new_handler oldHandler = currentHandler;
currentHandler = 0;
return oldHandler;
}
最后,Widget
的operator new
做以下事情
- 调用标准
set_new_hanlder
,告知Widge
的错误处理函数 - 调用
global operator new
,执行实际的内存分配 - 如果
global operator new
能够分配足够一个Widget
对象所用内存,Widget
的operator new
会返回一个指针,指向分配所得
以一个资源管理类实现上述操作:
class NewHandlerHolder
{
public:
explicit NewHandlerHolder(std::new_handler nh) : handler(nh){} // 取得目前的new-hanlder
~NewHandlerHolder()
{ std::set_new_handler(handler);}
private:
std::new_handler handler;
NewHandlerHolder(const NewHandlerHolder&); // 阻止copy
NewHandlerHolder & operator =(const NewHandlerHolder &);
};
然后实现Widget的opeator 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::set_new_handler(outOfMem); // 设定outOfMem为Widget的new-handing
Widget* pw1 = new Widget; // 如果内存分配失败调用outOfmem
std::string* ps = new std::string; // 如果内存分配失败调用global new-hanlding函数
Widget::set_new_handler(0); // 设定Widget的new-handing为null
Widget* pw2 = new Widget; // 如果内存分配失败,立刻抛出异常