1.new_handler函数:当operator new或operator new[]分配内存失败时调用的函数。
set_new_handler函数:试图分配更多内存以足够使用,成功时会返回,否则会抛出一个bad_alloc异常(或其派生类对象)或调用cstdlib的abort()或exit()函数强制退出。
参考资料:http://www.cplusplus.com/reference/std/new/set_new_handler/
http://www.cplusplus.com/reference/std/new/new_handler/
2.当operator new分配内存失败(不足)时,会先调用一个客户指定的错误处理函数new-handler,然后抛出一个异常(旧式的行为是返回一个空指针)。客户使用set_new_handler来设定该行为,它是声明于
namespace std{ typedef void (*new_handler)();//指向函数的指针
new_handler set_new_handler(new_handler p) throw();//获得分配内存失败时应该被调用的函数指针,并返回本函数在调用前正在执行(但马上就要被替换)的new-handler函数指针
} 当operator new无法满足内存申请时,会不断调用new-handler函数直到内存够用。
注:operator new诸函数与new不同,前者是标准程序库中的实现,它是一个函数,但不是重载,而后者是一个表达式。
3.一个设计良好的new-handler函数必须做以下事情:
4.为支持类专属的new-handler,可以在类中声明需要的new_handler函数,并提供类专属的set_new_handler(指定new-handler)和operator new(确保在分配类对象内存的过程中以该类专属的new-handler替换全局的new-handler)版本。具体过程为:
(1)声明一个类型为new_handler的静态成员函数,用以指向该类的new-handler。例如:
1: class Widget{
2:
3: public:
4:
5: static std::new_handler set_new_handler(std::new_handler p) throw();
6:
7: static void* operator new(std::size_t size) throw(std::bad_alloc);
8:
9: private:
10:
11: static std::new_handler currentHandler;
12:
13: };
注意static成员必须在类外定义(除非是const且是int型),所以需要这么写:
std::new_handler Widget::currentHandler = 0;
(2)set_new_handler函数将它获得的指针存储起来,然后返回调用前存储的指针(与标准set_new_handler)相同:
std::new_handler Widget::set_new_handler(std::new_handler p) thorw(){
std::new_handler oldHandler = currentHandler;
currentHandler = p
return oldHandler;
}
(3)使该类的operator new做以下事情:
5.实例:从资源处理类开始,只有基础性的RAII操作,在构造过程中获得一笔资源,并在析构过程中归还:
class NewHandlerHolder{ public: explicit NewHandlerHolder(std::new_handler nh) : handler(nh){} //取得目前的new-handler ~NewHandlerHolder(){ std::set_new_handler(handler); } //释放new-handler private: std::new_handler handler; //保存new-handler NewHanlderHolder(const NewHandlerHolder&); //阻止复制行为 NewHandlerHolder& operator=(const NewHandlerHolder&); };
假设有一个类Widget,它实现operator new的方法:
void *Widget::operator new(std::size_t size) throw(std::bad_alloc){ NewHandlerHolder h(std::set_newHandler(currentHandler));//安装Widget的new-handler return ::operator new(size); //分配内存或抛出异常,恢复全局new-handler }
Widget的客户应该类似如下方式new-handler:
void outOfMem(); //声明内存分配失败时的处理函数 Widget::set_new_handler(outOfMem); //将上面的函数设定为Widget的new-handler函数 Widget* pw1 = new Widget; //如果内存分配失败,调用outOfMem() std::string* ps = new std::string;//如果内存分配失败,调用全局的new-handler函数(如果有的话) Widget::set_new_handler(0); //设定Widget专属的new-handler为空,失去专属版本 Widget* pw2 = new Widget; //如果内存分配失败,则抛出异常
可以将上述方法抽象,建立mixin风格的基类,它允许派生类继承单一特定能力(这里是“设定该类专属的new-handler“),并把该基类抽象为模板类以提高复用性。
template<typename T> //mixin风格的基类,用以支持类专属的set_new_handler class NewHandlerSupport{ 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); ... //其他的operator new版本 private: static std::new_handler currentHandler; }; template<typename T> std::new_handler NewHandlerSupport::set_new_handler(std::new_handler p) throw(){ std::new_handler oldHandler = currentHandler; currentHandler = p; return oldHandler; } template<typename T> void* NewHandlerSupport ::operator new(std::size_t size) throw(std::bad_alloc){ NewHandlerHolder h(std::set_new_handler(currentHandler)); return ::operator new(size); } //以下将每一个currentHandler初始化为null template<typename T> std::new_handler NewHandlerSupport ::currentHandler = 0;
对于一个具体的类,添加专属的set_new_handler就很容易了:继承。
class Widget : public NewHandlerSupport{ ... //和先前一样,但不必声明set_new_handler或operator new };
6.旧式的operator new在内存分配失败后返回null,为了兼容旧代码,C++提供了另
一形式的operator new,称为nothrow形式,在new使用的场合使用了nothrow对象(定义于头
文件中)。建议不要使用。