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++>>
转载请注明。