码迷,mamicode.com
首页 > 其他好文 > 详细

定制new 和 delete

时间:2014-10-01 20:15:01      阅读:137      评论:0      收藏:0      [点我收藏+]

标签:blog   使用   for   strong   文件   sp   div   c   on   

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_alloc
if(pw1==0) //这个判断一定会失败

Widget *pw2=new (std::nothrow) Widget;//如果分配失败,返回0
if(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 new
public:
    ........
};

Derived *p=new Derived;//这里调用base::operator new

void* 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

标签:blog   使用   for   strong   文件   sp   div   c   on   

原文地址:http://www.cnblogs.com/jianxingzhe/p/4003395.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!