首页 > 代码库 > 定制new 和 delete

定制new 和 delete

1、了解new-handler的行为

当operator new 抛出异常以反映一个未满足的内存需求之前,他会先调用一个客户指定的错误处理函数,一个所谓的new-handler。为了指定这个“用以处理内存不足”的函数,客户必须调用set_new_handler,那个声明于<new>的一个标准程序库函数:

namespace std{      typedef void (*new_handler)();      new_handler set_new_handler(new_handler p) throw();        }

注:“throw()”是一份异常明细,表示该函数不抛出任何异常。

你可以这样使用set_new_handler:

//以下是当operator new 无法满足分配足够内存时,应该被调用的函数void outOfMem(){      std::cerr << "Unable to satisfy request for memory!\n";      std::abort();   }

当operator new 无法满足内存申请要求时,他会不断调用new_handler函数,直到找到足够内存。设计一个良好的new_handler函数必须做以下事情:

(1) 让更多的内存可被使用

  这边造成operator new 内下一次内存分配动作可以成功。实现这一策略的一个做法是,程序一开始执行就分配一大块内存,而后当new_handler第一次被调用,就将它们释还给程序使用。

(2) 安装另一个new_handler

  如果目前这个new_handler无法取得更多内存,或许它知道那个new_handler由此能力

(3) 卸除new_handler

  也就是将null指针传给set_new_hanlder。一旦没有安装任何new_handler,operator new会在内存分配不成功时抛出异常。

(4) 抛出bad_alloc(或派生自bad_alloc)的异常

  这样的异常不会被operator new捕捉,因此会被传播到内存所求处。

(5) 不反回

  通常调用abort或exit直接退出程序。

当你希望以不同方式处理内存分配情况失败情况的时候,你希望是被分配物属于哪个class而定。C++并不支持class专属之new-handlers,你可以自己实现出这种行为。

 

class Widget{public:    static std::new_handler set_new_handler(std::new_handler)throw();     static void* operator new(std::size_t size)throw(std::bad_alloc);private:    static std::new_handler currentHandler; }// Static 成员必须在class定义式外被定义(除非它们是const 而且是整数型)std::set_handler Widget::currentHandler=0;//在class实现文件内初始化为null;//Widget 内的set_new_handler 函数会将它获得的指针存储起来,然后返回先前存储的指针。std::new_handler Widget::set_new_handler(std::new_handler p)trow(){    std::new_handler oldHandler = currentHandler;    currentHandler=p;    return oldHandler;}

使用资源处理类来管理资源

class NewHandlerHolder{public:    explicit NewHandlerHolder(std::new_handler nh):handler(nh){}//获得当前的new_handler    ~NewHandlerHolder(){std::set_new_handler(handler);}//释放它private:    std::new_handler handler;    NewHandlerHolder(const NewHandlerHolder&); //阻止编译器自动生成拷贝构造函数    NewHandlerHolder& operator=(const NewHandlerHolder&); 阻止编译器自动生成赋值操作};

Widget operator new的实现:

void* Widget::operator new(std::size_t size)throw(std::bad_alloc){    NewHandlerHolder h(std::set_new_handler(currentHandler));    return std::operator new(size);}

Widget的客户应该类似这样使用其new_handling:

void outOfMem();//函数声明,此函数在Widget对象分配内存失败时被调用
Widget::set_new_handler(outOfMem);//安装new_handler函数
Widget* pw1=new Widget;//如果内存分配失败,调用outOfMem
std::string *ps=new std::string;//如果内存分配失败调用global new_handler函数(如果有的话)Widget::set_new_handler(0);//设定Widget专属的new_handler函数为null
Widget* pw2=new Widget;//如果内存分配失败,立刻抛出异常

可以将上面的代码设计成“mixin”风格的base class用以支持class专属的set_new_handler

template<typename T>class NewHandlerSupport{public:    static std::new_handler set_new_handler(std::new_handler)throw();     static void* operator new(std::size_t size)throw(std::bad_alloc);private:    static std::new_handler currentHandler; };
template<typename T>static std::new_handler NewHandlerSupport<T>::set_new_handler(std::new_handler)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 std::operator new(size);
}
template<typename T>
std::new_handler NewHandlerSupport<T>::currentHandler = 0;

class Widget:public NewHandlerSupport<Widget>{
....//和原来一样,但是不用声明set_new_handler 和 operator new
}

 旧的标准要求operator new 在无法分配足够多的内存时,返回null。新一代的operator new 则应该抛出bad_alloc异常。

class Widget{.....}Widget *pw1=new Widget;//如果分配失败,抛出bad_allocif(pw1==0) //这个判断一定会失败Widget *pw2=new (std::nothrow) Widget;//如果分配失败,返回0if(pw2==0) //这个测试可以成功

Nothrow() new 对异常的墙纸保证性并不高,它可以保证operator new不抛出异常,但是如果Widget构造函数中有可能又执行了一次new操作,这样还是会抛出异常。

请记住:

(1) set_new_handler 允许客户指定一个函数,在内存分配失败时被调用

(2) Notrow new 是一个颇为局限的工具,以为它适用于内存分配;后继的构造函数调用还是可能抛出异常。  

2、operator new伪代码分析 

void* operator new (std::size_t size) throw(std::bad_alloc){    using namespace std;    if(size == 0){  //如果size为0,将它视为1bytes申请          size=1;     }    while(true)    {        尝试分配size bytes        if(分配成功)            return  (一个指向分配内存的指针);        //分配失败,找出目前的new_handler函数        new_handler globalHandler = set_new_handler(0);        set_new_handler(globalHandler);        if(globalHandler) (*globalHandler)();        else throw std::bad_alloc();       }}

当申请内存大小为0byte,将申请量视为1byte,有助于简化语言其他部分。

在上述代码中,将new_handling函数指针设为null而后有立刻恢复原样,那是因为没有其他办法可以直接获取new_handling函数指针,所以必须调用set_new_handler找出它来。

 

3、class专属版的new/delete可以处理“比正确大小更大的申请”

class Base{public:    static void* operator new(std::size_t size)throw(std::bad_alloc);    static void operator delete(void* rawMemory,std::size_t size) throw();    ......};class Derived : public Base{ //假设Derived没有申明operator newpublic:    ........};Derived *p=new Derived;//这里调用base::operator newvoid* Base::operator new(std::size_t size) throw(std::bad_alloc){    if(size!=sizeof(Base))       return std::operator new(size);  //如果大小错误,调用标准的operator new 处理    ........}void Base::operator delete(void* rawMemory,std::size_t size) throw(){    if(rawMemory ==0) return ; //检查null指针    if(size!=sizeof(Base)){         std::operator new(size); //如果大小出错,另标准版的operator delete处理此申请        return ;        }}

  

  

  

 

  

定制new 和 delete