当我看到C++ Primer 第五版中介绍new和operator操作符的时候我非常惊叹C++的强大功能,于是趁热打铁读了More Effective C++中关于new和delete操作的有关章节,本以为自己对new和delete操作符及其内存机制已然了解的足够深入,但当我读到《STL源码剖析》的时候,又一次傻眼了,什么叫new_handler,set_new_handler又有什么作用,当内存分配失败时到底发生了什么?再一次,被C++强大的功能给震撼了!!!于是怀着对知识的渴望,打开了Effective C++ 3e来阅读有关new和delete的有关章节!
条款49, 50, 51, 52。
本章来源《Effective C++》3e
概述:
条款49:了解new-handler的行为
测试函数
#include<iostream>
#include<new>
void OutOfMemory()
{
std::cerr << "unable to satisfy request for memory\n";
std::abort();
}
int main()
{
std::set_new_handler(OutOfMemory);
while (true)
{
int *pBigDataArray = new int[100000000L];
}
return 0;
}
#include<iostream>
#include<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 currentHander; //new_handler只是一个函数指针,typedef void (*new_handler)()
};
//static成员必须在class定义时之外被定义(除非它们是const而且是整数型,见条款2),所以需要这么写:
std::new_handler Widget::currentHander = 0; //在class实现文件内初始化为null
//Widget内的set_new_handler函数会将它获得的指针存储起来,然后返回先前(在调用之前)存储的指针,这也正是标准版set_new_handler的作为
std::new_handler Widget::set_new_handler(std::new_handler p) throw()
{
std::new_handler oldHandler = currentHander;
currentHander = p;
return oldHandler;
}
#include<iostream>
#include<new>
class NewHandlerHolder{
public:
explicit NewHandlerHolder(std::new_handler nh) :handler(nh){} //取得目前的new_handler
~NewHandlerHolder(){ std::set_new_handler(handler); } //释放它,回复原本的global-new-handler
private:
std::new_handler handler;
NewHandlerHolder(const NewHandlerHolder&); //阻止copying
NewHandlerHolder &operator=(const NewHandlerHolder &);
};
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 currentHander; //new_handler只是一个函数指针,typedef void (*new_handler)()
};
//static成员必须在class定义时之外被定义(除非它们是const而且是整数型,见条款2),所以需要这么写:
std::new_handler Widget::currentHander = 0; //在class实现文件内初始化为null
//Widget内的set_new_handler函数会将它获得的指针存储起来,然后返回先前(在调用之前)存储的指针,这也正是标准版set_new_handler的作为
std::new_handler Widget::set_new_handler(std::new_handler p) throw()
{
std::new_handler oldHandler = currentHander;
currentHander = p;
return oldHandler;
}
void *Widget::operator new(std::size_t size) throw(std::bad_alloc)
{
NewHandlerHolder h(std::set_new_handler(currentHander)); //安装Widget的new_handler分配内存或者抛出异常
return ::operator new(size); //回复global new_handler,如果失败,首先恢复原本的global-new-handler,再传播异常,成功的话返回一个指针
}
Widget的客户应该类似这样使用new-handling:
void outOfMem();//函数声明,此函数在Widget对象分配失败时被调用
Widget::set_new_handler(outOfMem);//设定outOfMem为Widget的new-handling函数
Widget *pw1 = new Widget;//如果内存分配失败,调用outOfMem
std::string *ps = new std::string;//如果内存分配失败,调用global new-handling函数(如果有的话)
Widget::set_new_handler(0);//设定Widget专属的new-handling函数为null
Widget *pw2 = new Widget;//如果内存分配失败,立刻抛出异常(class Widget并没有专属的new-hangling函数)
#include<iostream>
#include<new>
class NewHandlerHolder{
public:
explicit NewHandlerHolder(std::new_handler nh) :handler(nh){} //取得目前的new_handler
~NewHandlerHolder(){ std::set_new_handler(handler); } //释放它,回复原本的global-new-handler
private:
std::new_handler handler;
NewHandlerHolder(const NewHandlerHolder&); //阻止copying
NewHandlerHolder &operator=(const NewHandlerHolder &);
};
template<typename T> //“mixin”风格的base class,用以支持class专属的set_new_handler
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);
//其他的operator new版本----见条款52
private:
static std::new_handler currentHandler;
};
//将每一个currentHandler初始化为null
template<typename T>
std::new_handler NewHandlerSupport<T>::currentHandler = 0;
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);
}