1993年前,c++一直要求在内存分配失败时operator new要返回0,现在则是要求operator new抛出std::bad_alloc异常。很多c++程序是在编译器开始支持新规范前写的。c++标准委员会不想放弃那些已有的遵循返回0规范的代码,所以他们提供了另外形式的operator new(以及operator new[])以继续提供返回0功能。这些形式被称为“无抛出”,因为他们没用过一个throw,而是在使用new的入口点采用了nothrow对象:
class widget { ... }; widget* pw1 = new widget;// if allocated failed throw std::bad_alloc if (pw1 == 0) ... // must be failed widget* pw2 = new (nothrow)widget; // if allocated failed return 0(NULL) if (pw2 == 0) ... // maybe successful
operator new默认情况下如果不能满足内存分配请求时会抛出的异常类型std::bad_alloc。因此,如果你不愿意看到诸如:widget* pw2 = new (nothrow) widget; 这样带着nothrow的operator new,最简单的方法是使用set_new_handler自定义newhandler函数。最简单的例子如下:
// main.cpp (vc9.0) #include "stdafx.h" #include<new> #include<iostream> #include<stdlib.h> using namespace std; void __cdecl newhandlerfunc() { cerr << "Allocate failed." << endl; abort(); } int main() { set_new_handler (newhandlerfunc); while (1) { int* p = new int[500000000]; // based on own machine's memory size cout << "Allocating 500000000 int." << endl; } }
newhandlerfunc函数中没有参数也没有返回值,可提供的信息有限,这样的全局通用函数在c++项目中所能起的作用不大。所以,鉴于c++本身的面向对象的设计,我们考虑每个类有自己的newhandler,与全局的newhandler共存。比较简单的例子如下:
// main.cpp (vc9.0) #include "stdafx.h" #include<new> #include<iostream> #include<stdlib.h> using namespace std; void __cdecl newhandlerfunc() { cerr << "Allocate failed." << endl; abort(); } void __cdecl xnewhandlerfunc() { cerr << "Allocate x object failed" << endl; abort(); } class x { public: x() { while (1) { int* p = new int[500000000]; cout << "Allocating 500000000 int." << endl; } } static new_handler set_new_handler(new_handler p) { new_handler oldhandler = currenthandler; currenthandler = p; return oldhandler; } static void * operator new(size_t size) { // setup x's new_handler, now it replaces global handler new_handler globalhandler = set_new_handler(currenthandler); void *memory; try { memory = ::operator new(size); // try to allocate memory } catch (std::bad_alloc&) // handled by x's new_handler { set_new_handler(globalhandler); // recover old new_handler throw; // rethrow exception } // allocate normally std::set_new_handler(globalhandler); // recover old new_handler return memory; } private: static new_handler currenthandler; }; new_handler x::currenthandler = NULL; int main() { //set_new_handler(newhandlerfunc); //while (1) //{ // int* p = new int[500000000]; // cout << "Allocating 500000000 int." << endl; //} x::set_new_handler(xnewhandlerfunc); x* p = new x; return 0; }
如果每个类都这样地写一遍,感觉有些代码重复,所以可能想到会写一个基类,让所有的子类可以继承set_new_handler和operator new功能,但要使每个子类有不同的currenthandler数据成员,一个比较简单的方法就是设计一个模板。
例子如下:
// SetNewHandler.h (vc9.0) #include<new> #include<iostream> #include<stdlib.h> using namespace std; template<class t> class newhandlersupport { public: static new_handler set_new_handler(new_handler p); static void * operator new(size_t size); private: static new_handler currenthandler; }; template<class t> new_handler newhandlersupport<t>::set_new_handler(new_handler p) { new_handler oldhandler = currenthandler; currenthandler = p; return oldhandler; } template<class t> void * newhandlersupport<t>::operator new(size_t size) { new_handler globalhandler = set_new_handler(currenthandler); void *memory; try { memory = ::operator new(size); } catch (std::bad_alloc&) { set_new_handler(globalhandler); throw; } std::set_new_handler(globalhandler); return memory; } template<class t> new_handler newhandlersupport<t>::currenthandler = NULL;
// main.cpp (vc9.0) #include "stdafx.h" #include "SetNewHandler.h" #include<new> #include<iostream> #include<stdlib.h> using namespace std; void __cdecl newhandlerfunc() { cerr << "Allocate failed." << endl; abort(); } void __cdecl xnewhandlerfunc() { cerr << "Allocate x object failed" << endl; abort(); } void __cdecl ynewhandlerfunc() { cerr << "Allocate y object failed" << endl; abort(); } class x { public: x() { while (1) { int* p = new int[500000000]; cout << "Allocating 500000000 int." << endl; } } static new_handler set_new_handler(new_handler p) { new_handler oldhandler = currenthandler; currenthandler = p; return oldhandler; } static void * operator new(size_t size) { // setup x's new_handler, now it replaces global handler new_handler globalhandler = set_new_handler(currenthandler); void *memory; try { memory = ::operator new(size); // try to allocate memory } catch (std::bad_alloc&) // handled by x's new_handler { set_new_handler(globalhandler); // recover old new_handler throw; // rethrow exception } // allocate normally std::set_new_handler(globalhandler); // recover old new_handler return memory; } private: static new_handler currenthandler; }; new_handler x::currenthandler = NULL; class y: public newhandlersupport<y> { public: y() { while (1) { int* p = new int[500000000]; cout << "Allocating 500000000 int." << endl; } } }; int main() { //set_new_handler(newhandlerfunc); //while (1) //{ // int* p = new int[500000000]; // cout << "Allocating 500000000 int." << endl; //} //x::set_new_handler(xnewhandlerfunc); //x* p = new x; y::set_new_handler(ynewhandlerfunc); y* p = new y; return 0; }
参考:<<Effective C++>>
转载请注明。