7、为内存不足的状况预做准备
1、使用new时内存不足
2、如何处理内存不足
#define NEW(PTR, TYPE) \
try {(PTR) = new TYPE;}\
catch(std::bad_alloc&) {assert(0);}
如上,assert()在cassert头文件,是一个宏,只有在debug模式下才有用,会检查接收的算式结果是否为非0值,如果不是会发出错误信息并调用abort。但是,在非debug模式下也有可能发生这种情况,且上述只针对new的一种形式,即new T。
typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();
如上,new_handler是一个typedef,是一个函数指针,指向的函数没有参数没有返回值。而set_new_handler是一个函数,入参与返回值均为new_handler,入参指向内存不足时调用的函数,返回值指向之前的new_handler。可以这样使用set_new_handler():
void noMoreMemory()
{
cerr << "Unable to satisfy request for memory\n";
abort();
}
int main()
{
set_new_handler(noMoreMemory);
int *pBigDataArray = new int[100000000];
...
}
当operator new无法配置内存时,他会不停地调用new_handler,直到找到内存。故一个良好的new_handler必须完成以下事情之一:
3、class专属的operator new
class X
{
public:
static new_handler set_new_handler(new_handler p);
static void * operator new(size_t size);
private:
static new_handler currentHandler;
}
为class X处理内存配置失败的问题,应将这些设为类的static成员,初始化在定义之外,如可以在实现文件中。
new_handler X::currentHandler;//初始化为0, 即NULL
set_new_handler将获得的new_handler保存下来,返回之前保存的new_handler:
new_handler X::set_new_handler(new_handler p)
{
new_handler oldHandler = currentHandler;
currentHandler = p;
return oldHandler;
}
最后,operator new完成这些事情:
1.调用std::set_new_handler,将class的new_handler设为新的new_handler,将原来的new_handler保存为globalHandler。
2.调用::operator new,若内存配置失败,调用设置好的new_handler,若最终还是没有配置内存,则抛出std::bad_alloc的异常,被X::operator new捕捉,调用std::set_new_handler,将new_handler恢复为globalHandler保存的new_handler,再将异常传播出去并结束函数。
3.若::operator new内存配置成功,同样调用std::set_new_handler,将new_handler恢复为globalHandler保存的new_handler。然后传回一个指针,指向那块内存。
void * X::operator new(size_t size)
{
new_handler globalHandler = std::set_new_handler(currentHandler);
void *memory;
try
{
memory = ::operator new(size);
}
catch(std::bad_alloc&)
{
std::set_new_handler(globalHandler);
throw;
}
std::set_new_handler(globalHandler);
return memory;
}
以上,事实上,可以添加模板来实现代码的重用,只需添加template<typename T>,修改类型即可。然后定义的类由此模板类派生即可。这样的操作方便简单,但有可能引入多重继承的一些问题,这里暂且不提。
因此,使用operator new时务必关注内存配置失败的问题,处理此问题的方法之一是判断返回值是否为0,这对于抛出异常形式的new会测试失败;而另外方法之一是用set_new_handler,他对于抛出异常形式的new和nothrow形式的new都适用。