zoukankan      html  css  js  c++  java
  • 定制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!
    ";
          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) Nothrow 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 ;    
        }
    }
    

      

      

      

     

      

  • 相关阅读:
    2013-06-28,“万能数据库查询分析器”在中关村本月数据库类下载排行中重返前10位
    Oracle 存储过程
    强化学习精要:核心算法与TensorFlow实现
    深入理解TensorFlow:架构设计与实现原理
    Vue.js实战
    TensorFlow机器学习实战指南
    深入浅出React和Redux
    Flutter技术入门与实战
    TensorFlow:实战Google深度学习框架
    深度学习:一起玩转TensorLayer
  • 原文地址:https://www.cnblogs.com/jianxingzhe/p/4003395.html
Copyright © 2011-2022 走看看