author:
- luixiao1223
title: 了解new-handler的行为
tips
STL 的heap时容器所拥有的分配器对象管理的,不是new和delete管理.
什么是new-handler
new抛出一场以反映一个未获满足的内存需求之前,会先调用一个用户指定的错误处理函数.这个就是new-handler.
namespace std {
typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();
}
比如你可以这样使用
void outOfMem()
{
std::cerr << "Unable to satisfy request for memory\n";
std::abort();
}
int main()
{
std::set_new_handler(outOfMem);
int *pBigDataArray = new int[100000000L];
}
当new无法满足内存申请时,他会不断调用new-handler函数
那么一个良好的new-handler应该具备
- 让更多的内存可被利用
- 安装另一个new-handler应该
- 写在new-handler
- 抛出
bad_alloc
会被抛出到外面 - 不反回,直接调用abort和exit
应用
我们想让不同的class具有不同的内存处理方式.我们需要class自己来定义自己的new
operator.那么标准的操作为.
- 设置handler
- new操作
- 恢复现场
class Widget {
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);
private:
static std::new_handler currentHandler;
};
std::new_handler Widget::currentHandler = 0;
std::new_handler Widget::set_new_handler(std::new_handler p) throw()
{
std::new_handler oldHandler = currentHandler;
currentHandler = p;
return oldHandler;
}
Handler Holder
class NewHandlerHolder {
public:
explicit NewHandlerHolder(std::new_handler nh)
: handler(nh) {}
~NewHandlerHolder()
{ std::set_new_handler(handler); }
private:
std::new_handlerhandler;
NewHandlerHolder(const NewHandlerHolder&);
NewHandlerHolder&
operator=(const NewHandlerHolder&);
};
定义自己的new operator
void* Widget::operator new(std::size_t size) throw(std::bad_alloc)
{
NewHandlerHolder
h(std::set_new_handler(currentHandler));
return ::operator new(size); //调用全局的new
// 由于使用了 NewHandlerHolder ,这句之后将进行恢复现场.
}
使用的方法为
void outOfMem();
Widget::set_new_handler(outOfMem);Widget *pw1 = new Widget;
std::string *ps = new std::string;
Widget::set_new_handler(0);
Widget *pw2 = new Widget;
一个更好的版本,使用继承
template<typename T>
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);
private:
static std::new_handler currentHandler;
};
template<typename T>
std::new_handler
NewHandlerSupport<T>::set_new_handler(std::new_handler p) throw()
{
std::new_handler oldHandler = currentHandler;
currentHandler = p;
return oldHandler;
}
template<typename T>
void* NewHandlerSupport<T>::operator new(std::size_t size)
throw(std::bad_alloc)
{
NewHandlerHolder h(std::set_new_handler(currentHandler));
return ::operator new(size);
}
template<typename T>
std::new_handler NewHandlerSupport<T>::currentHandler = 0;
这样每一个不同的class都有自己独一无二的holder.
这正式T的作用.我们虽然没有使用到T,但是T使得我们有了独一无二的holder.
class Widget: public NewHandlerSupport<Widget> {
}; //Widget继承自一个模板,这个模板的类型参数为Widget.也就是它自己.
One More Thing
class Widget { ... };
Widget *pw1 = new Widget;
if (pw1 == 0)
...
Widget *pw2 = new (std::nothrow) Widget;
if (pw2 == 0)
...
其实不抛出异常是不可能的.但是你使用std::nothrow只是保证第一层new不抛出异常.失败就返回null.但是Widget的构造函数里面可能也会进行new操作这里面的异常抛出行为就不会受到你的控制.