• 定制类自己的的new_handler


      C++中的new操作符首先使用operator new函数来分配空间,然后再在此空间上调用类的构造函数构造对象。当operator new无法分配所需的内存空间时,默认的情况下会抛出一个bad_alloc异常,在抛出这个异常之前,如果用户指定了错误处理函数即new_handler,则程序会先执行new_handler函数进行错误处理。为了设置这个错误处理函数,我们需要调用set_new_handler函数,它在std命名空间内的情况如下所示

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

      set_new_handler函数承若不抛出任何异常。该函数接受一个new_handler函数指针,且返回一个这样的指针。pNew代表的是如果operator new发生失败,采用的错误处理函数是pNew所指向的函数,set_new_handler函数返回的是以前的错误处理函数指针。比如,

    void myErrorHandler(){
        cerr<<"can't need the request for memory";
        abort();
    }
    int main(){
        std::set_new_handler(myErrorHandler);
        int *ptr=new int[1000000000L];
        delete [] ptr;
        return ;
    }

      当出现无法满足内存分配要求的时候,程序会不断的调用myErrorHandler函数进行内存的分配,如果错误处理函数中没有abort(),终端会不断的打印错误提示。设置全局的set_new_handler,只要出现operator new失败,都会调用该错误处理函数。但是如果我们需要给不同的类设置不同的错误处理函数时,该怎么办呢?也就是说我们需要定制类自己的set_new_handler函数。

    class Widget{
        public:
            static std::new_handler set_new_handler(std::new_handler pNew) throw ();
            static void* operator new(std::size_t size)throw (std::bad_alloc);
        private:
            static std::new_handler currentHandler;
            int array[100000000L];//为了容易让new Widget出现异常
    };
    std::new_handler Widget::currentHandler=0;

      如上所示,类Widget需要定义自己的new_handler变量,而且需要定义自己的set_new_handler函数和operator new函数,set_new_handler函数比较简单,参照全局的set_new_handler函数的行为即可。

    std::new_handler Widget::set_new_handler(std::new_handler pNew){
        std::new_handler oldHandler=currentHandler;
        currentHandler=pNew;
        return oldHandler;
    }

      函数set_new_handler设置新的new_handler,并且返回以前的new_handler。

      那么Widget类中的operator new函数应该怎么定义呢?

      首先,它应该设置本类的new_handler,怎么给Widget设置自己的new_handler并让其正常工作呢?调用std::set_new_handler即可,类中的currentHandler可作为其参数;

      其次,调用std::operator new函数请求分配内存,但是,这里可能存在一个资源泄露的问题,如第一步,我们将全局的set_new_handler设置成类Widget的错误处理函数,如果分配失败,我们怎么将原来的new_handler再设置回去呢?很显然的一种方法就是通过对象来管理new_handler,如下

      

    class NewHandlerHolder{
        public:
            explicit NewHandlerHolder(new_handler p):oldHandler(p){}
            ~NewHandlerHoler(){
                std::set_new_handler(oldHander);
            }
        private:
            std::new_handler oldHandler;
            NewHandlerHolder(const NewHandlerHolder&);
            NewHandlerHolder& operator=(const NewHandlerHolder&);
    };

      这是一个资源管理类,如果我们定义了一个NewHandlerHolder对象来管理原来的new_handler,当内存分配失败时,抛出异常且函数退栈,同时也会调用已经构造的对象的析构函数,当我们调用NewHandlerHolder的析构函数时,会重新将原来的new_handler设置回去。用于资源管理的类在较多情况下不允许进行赋值和复制构造的动作,因为我们希望这个资源管理的对象具有资源唯一的管理权。

      此时,我们就可以写出来Widget类中的operator new函数了。

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

      定义完所有所需的内容后,我们可以使用定制的new_handler。

    int main(){
        Widget::set_new_handler(myErrorHandler);
        //Widget::set_new_handler(0);
        while(1)
            Widget *ptr=new Widget;
    }

      运行结果如下图

      

       将注释的解注,运行结果没有"can't need the request for memory"。

       以上思想就能为各个类编写自己的定制的new_handler,但是为每个需要编写定制new_handler的类写如此相似的代码,似乎有点不合理,为了避免代码重复,我们可以使用模板

      

    template<typename T>
    class NewHandlerSupport{
        public:
            static std::new_handler set_new_handler(std::new_handler newHandler)throw ();
            static void* operator new(std::size_t size)throw (std::bad_alloc);
        private:
            static std::new_handler currentHandler;    
    };
    template<typename T>
    void* NewHandlerSupport<T>::operator new(std::size_t size){
        NewHandlerHolder temp(std::set_new_handler(currentHandler));
        return ::operator new(size);
    }
    template<typename T>
    std::new_handler NewHandlerSupport<T>::set_new_handler(std::new_handler newHandler)throw(){
        std::new_handler oldHander=currentHandler;
        currentHandler=newHandler;
        return oldHandler;
    }
    template<typename T>
    std::new_handler NewHandlerSupport<T>::currentHandler=0;

      如果我们希望类widget获得定制的new_handler行为,只需要将widget类以public继承NewHandlerSupport<widget>即可。我们只是希望获得不同模板参数所带来的不同的static std::new_handler currentHandler,我们需要用它来定制各个类的new_handler。

  • 相关阅读:
    asp.net 发送邮件
    效控制C#中label输出文字的长度,自动换行
    无法连接到WMI 提供程序 请注意,你只能使用SQL Server 配置管理器来管理SQL Server 2005服务器。找不到指定的模块。[0x8007007e]
    查询区分大小写
    ASP.NET母版页引用外部css和js文件的写法
    VS2008 Debugging Breakpoint 补丁
    firefox下获得焦点
    IE打开出现windows找不到文件'(null)'解决方法Vinzipblog文之巴博客
    邪恶的web上下键焦点移动
    jQuery对下拉框Select操作总结
  • 原文地址:https://www.cnblogs.com/sjinsa/p/4600310.html
Copyright © 2020-2023  润新知