内存分配失败时,老的C++编译器返回null空指针;新的则抛出bad_alloc异常。
在此之前,operator new要先调用一个函数。此函数可由用户定制,叫做new_handler。由该函数处理内存不足这个问题。
直到找到合适的内存为止,将反复调用该函数。
该函数由std::set_new_handler注册给operator new:
namespace std { typedef void (*new_handler)(); new_handler set_new_handler(new_handler p) throw(); }
一个合格的new_handler必须满足下面之一:
● 找出可用内存。可在程序启动时先申请一大块内存,然后在内存不足时慢慢分配给程序。
● 如果无法找到可用内存,将找内存这项大任代理给其它的new_handler。
● 连代理人也找不到,那么 set_new_handler(null)。这样将抛出bad_alloc异常。
● 直接抛出异常。
● 用abort或exit终止程序。
比较简单的一种用法:
#include <new> void out_of_memory() { std::cerr << "Unable to satisfy request for memory\n"; std::abort(); // 内存不够时终止程序 } int main() { std::set_new_handler(out_of_memory); int *pBigDataArray = new int[100000000L]; ... }
另外一种比较麻烦的用法:
using std::new_handler; using std::size_t; using std::bad_alloc; // 使用管理器保证new_handler资源能够有效释放 class NewHandlerHolder { public: explicit NewHandlerHolder(new_handler h) // acquire current :handler(h) {} // new-handler ~NewHandlerHolder() // release it { std::set_new_handler(handler); } private: new_handler handler; NewHandlerHolder(const NewHandlerHolder&); // 禁止拷贝行为 NewHandlerHolder& operator=(const NewHandlerHolder&); }; class Widget { public: static new_handler set_new_handler(new_handler p) throw(); static void * operator new(size_t size) throw(bad_alloc); ... private: static new_handler currentHandler; }; new_handler Widget::currentHandler = 0; new_handler Widget::set_new_handler(new_handler p) throw() { new_handler oldHandler = currentHandler; currentHandler = p; return oldHandler; } void * Widget::operator new(size_t size) throw(bad_alloc) { NewHandlerHolder h(std::set_new_handler(currentHandler)); // 代理给某个new_handler return ::operator new(size); // 分配内存 } // 分配结束时或出现异常时,还原new_handler int main() { void out_of_memory(); Widget::set_new_handler(out_of_memory); Widget *pw1 = new Widget; // 内存不够时,调用out_of_memory std::string *ps = new std::string; // 内存不够时,调用全局的new_handler Widget::set_new_handler(0); // 禁掉Widget的new_handler Widget *pw2 = new Widget; // 内存不够时,直接抛出异常 }
最复杂的用法,定义一个CRTP基类(curiously recurring template pattern):
template<typename T> class NewHandlerSupport { public: static new_handler set_new_handler(new_handler p) throw(); static void * operator new(size_t size) throw(bad_alloc); ... private: static new_handler currentHandler; }; template<typename T> new_handler NewHandlerSupport<T>::set_new_handler(new_handler p) throw() { new_handler oldHandler = currentHandler; currentHandler = p; return oldHandler; } template<typename T> void* NewHandlerSupport<T>::operator new(size_t size) throw(bad_alloc) { NewHandlerHolder h(std::set_new_handler(currentHandler)); return ::operator new(size); } template<typename T> new_handler NewHandlerSupport<T>::currentHandler = 0; class Widget: public NewHandlerSupport<Widget> { ... };
下面的用法是新编译器对老代码的兼容式规定:
class Widget { ... }; Widget *pw1 = new Widget; // 1. 内存不够,抛出异常 if (pw1 == 0) ... // 1. 所以此处代码不能正常工作 Widget *pw2 =new (std::nothrow) Widget; // 2. 内存不够,返回空指针 if (pw2 == 0) ... // 2. 此处代码也许可以正常工作
但是,nothrow不见得总管用。比如在内存分配成功后,然后又在Widget::ctor里出现内存不够的情况,那么还是会抛出异常。