TOP

Item 49:new handler的行为(一)
2016-02-23 11:35:09 】 浏览:10126
Tags:Item new handler 行为

Item 49: Understand the behavior of the new-handler.

new申请内存失败时会抛出"bad alloc"异常,此前会调用一个由std::set_new_handler()指定的错误处理函数(”new-handler”)。

set_new_handler()

“new-handler”函数通过std::set_new_handler()来设置,std::set_new_handler()定义在 中:

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

throw()是一个异常声明,表示不抛任何异常。例如void func() throw(Exception1, Exception2)表示func可能会抛出Exception1Exception2两种异常。

set_new_handler()的使用也很简单:

void outOfMem(){
    std::cout<<"Unable to alloc memory";
    std::abort();
}
int main(){
    std::set_new_handler(outOfMem);
    int *p = new int[100000000L];
}

new申请不到足够的内存时,它会不断地调用outOfMem。因此一个良好设计的系统中outOfMem函数应该做如下几件事情之一:

  • 使更多内存可用;
  • 安装一个新的”new-handler”;
  • 卸载当前”new-handler”,传递nullset_new_handler即可;
  • 抛出bad_alloc(或它的子类)异常;
  • 不返回,可以abort或者exit

    类型相关错误处理

    std::set_new_handler设置的是全局的bad_alloc的错误处理函数,C++并未提供类型相关的bad_alloc异常处理机制。 但我们可以重载类的operator new,当创建对象时暂时设置全局的错误处理函数,结束后再恢复全局的错误处理函数。

    比如Widget类,首先需要声明自己的set_new_handleroperator 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 current;
    };
    
    // 静态成员需要定义在类的外面
    std::new_handler Widget::current = 0;
    std::new_handler Widget::set_new_handler(std::new_handler p) throw(){
        std::new_handler old = current;
        current = p;
        return old;
    }
    

    关于abort,exit,terminate的区别:abort会设置程序非正常退出,exit会设置程序正常退出,当存在未处理异常时C++会调用terminate, 它会回调由std::set_terminate设置的处理函数,默认会调用abort

    最后来实现operator new,该函数的工作分为三个步骤:

    1. 调用std::set_new_handler,把Widget::current设置为全局的错误处理函数;
    2. 调用全局的operator new来分配真正的内存;
    3. 如果分配内存失败,Widget::current将会抛出异常;
    4. 不管成功与否,都卸载Widget::current,并安装调用Widget::operator new之前的全局错误处理函数。

      重载operator new

      我们通过RAII类来保证原有的全局错误处理函数能够恢复,让异常继续传播。关于RAII可以参见Item 13。 先来编写一个保持错误处理函数的RAII类:

      class NewHandlerHolder{
      public:
          explicit NewHandlerHolder(std::new_handler nh): handler(nh){}
          ~NewHandlerHolder(){ std::set_new_handler(handler); }
      private:
          std::new_handler handler;
          NewHandlerHolder(const HandlerHolder&);     // 禁用拷贝构造函数
          const 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(current));
          return ::operator new(size);    // 调用全局的new,抛出异常或者成功
      }   // 函数调用结束,原有错误处理函数恢复
      

      使用Widget::operator new

      客户使用Widget的方式也符合基本数据类型的惯例:

      void outOfMem();
      Widget::set_new_handler(outOfMem);
      
      Widget *p1 = new Widget;    // 如果失败,将会调用outOfMem
      string *ps = new string;    // 如果失败,将会调用全局的 new-handling function,当然如果没有的话就没有了
      Widget::set_new_handler(0); // 把Widget的异常处理函数设为空
      Widget *p2 = new Widget;    // 如果失败,立即抛出异常
      

      通用基类

      仔细观察上面的代码,很容易发现自定义”new-handler”的逻辑其实和Widget是无关的。我们可以把这些逻辑抽取出来作为一个模板基类:

      template
             
               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); private: static std::new_handler current; }; template
              
                std::new_handler NewHandlerSupport
               
                ::current = 0; template
                
                  std::new_handler NewHandlerSupport
                 
                  ::set_new_handler(std::new_handler p) throw(){ std::new_handler old = current; current = p; return old; } template
                  
                    void * NewHandlerSupport
                   
                    ::operator new(std::size_t s  
      		
      Item 49:new handler的行为(一) https://www.cppentry.com/bencandy.php?fid=49&id=113263

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇C++ Primer 学习笔记_48_STL剖析.. 下一篇Item 45:使用成员函数模板来接受..