Effective C++ 笔记7: 对内存不足的处理
用 new 申请内存时有可能出现内存不足,从而导致申请失败的情况,这时就要采取相应的对策。大多数初学者都会觉得这没什么,毕竟有了虚拟内存技术,一般操作系统的可用内存都会在2G以上,不太可能出现内在不足,但这种可能毕竟是存在的,而一个好的程序就应该把所有可能存在的 bug 都去掉。
早期的C++标准要求 new 在申请失败的时候返回空指针,但现在的标准则使用 exception ,在申请失败的时候抛出一个 std::bad_alloc 类型的 exception。所以要对内存处理进行处理可以这么写:
try{
int * n=new int[100000000000];
}
catch(std::bad_alloc &){
cerr<<"not enough memory!"<<endl;
exit(1);
}
但是在代码中对每个 new 都进行 try 和 catch 显然不是最好的方法。C++给我们提供了另一种方法,即 out-of-memory-handling-function 。在 new 函数出现错误时,它会先调用一个错误处理函数,然后再把 std::bad-alloc 抛出来。而C++允许我们自己通过 set_new_handler() 设置这个错误处理函数,这个函数的原型为:
typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();
这里,new_handler是一个函数指针,它是一个无参数无返回值的函数指针,指向我们自己定义的错误处理函数。set_new_handler 函数接受一个新的错误处理函数的指针,并返回原来的函数指针。比如我们可以这么写:
// function to call if operator new can't allocate enough memory
void noMoreMemory()
{
cerr << "Unable to satisfy request for memory\n";
abort();
}
int main()
{
new_handler oldHandler=set_new_handler(noMoreMemory);
int *pBigDataArray = new int[100000000];
...
set_new_handler(oldHandler);
...
}
另外要强调的一点是,new 函数会重复调用错误处理函数,直到申请内在的操作成功,或是错误处理函数调用 abort() 或 exit()。
一般的错误处理函数可以做以下工作:
清理内存,以使下一次 new 能顺利进行。常见的做法是在程序开始时就申请一大块内存,然后在错误处理函数中把这块内在释放掉。
设置其它的错误处理函数。如果错误处理函数自己没办法释放更多内存了,那它可以调用 set_new_handler ,把控制权交给另一个错误处理函数。比如不能再释放内存时,可以把处理函数设成上面的 noMoreMemory,这样下一次错误处理时就会结束 new 操作。
把错误处理函数指针设成空,这样当 new 发现指针为空时就会抛出异常并结束操作。
抛出异常,这样也会结束 new 操作。
调用 abort 或 exit,abort 会结束 new 操作,而 exit 就会退出程序。
有时候我们希望对每一个类进行不同的处理,这时可以重载这个类的 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;
};
new_handler X::set_new_handler(new_handler p)
{
new_handler oldHandler = currentHandler;
currentHandler = p;
return oldHandler;
}
void * X::operator new(size_t size)
{
new_handler globalHandler = // install X's
std::set_new_handler(currentHandler); // handler
void *memory;
try { // attempt
memory = ::operator new(size); // allocation
}
catch (std::bad_alloc&) { // restore
std::set_new_handler(globalHandler); // handler;
throw; // propagate
} // exception
std::set_new_handler(globalHandler); // restore
// handler
return memory;
}
这么使用:
void noMoreMemory(); // decl. of function to
// call if memory allocation
// for X objects fails
X::set_new_handler(noMoreMemory);
// set noMoreMemory as X's
// new-handling function
X *px1 = new X; // if memory allocation
// fails, call noMoreMemory
string *ps = new string; // if memory allocation
// fails, call the global
// new-handling function
// (if there is one)
X::set_new_handler(0); // set the X-specific
// new-handling function
// to nothing (i.e., null)
X *px2 = new X; // if memory allocation
// fails, throw an exception
// immediately. (There is
// no new-handling function
// for class X.)
上面的代码显然应该复用,所以我们这么写:
template<class T> // "mixin-style" base class
class NewHandlerSupport { // for class-specific
public: // set_new_handler support
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 =
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;
}
// this sets each currentHandler to 0
template<class T>
new_handler NewHandlerSupport<T>::currentHandler;
class X: public NewHandlerSupport<X> {
... // as before, but no declarations for
}; // set_new_handler or operator new
class Y: public NewHandlerSupport<Y> {
... // as before, but no declarations for
}; // set_new_handler or operator new
这里使用模板类是因为 currentHandler 是一个静态成员,如果不用模板的话那么类 X 和类 Y 就会共用一个 currentHandler 。
这里不是很懂.能讲讲吗?谢谢!
typedef void (*new_handler)();
定义new_handler指针的类型,这是函数指针的定义方式,相当于这样一个函数:
void handler();
然后new_handler是指向这个函数的指针
new_handler set_new_handler(new_handler p) throw();
这是声明set_new_handler函数,它的参数跟返回值都是new_handler指针类型的,在错误时会抛出一个异常