zoukankan      html  css  js  c++  java
  • c++中的set_new_handler和new_handler

    当operator new申请一个内存失败的时候,它会进行如下的处理步骤:
        1、如果存在客户指定的处理函数,则调用处理函数(new_handler),如果不存在则抛出一个异常。

        2、继续申请内存分配请求。
        3、判断申请内存是否成功,如果成功则返回内存指针,如果失败转向处理步骤1


    为了自定义这个“用以处理内存不足”的函数new_handler,用户可以调用set_new_handler进行设置

    这两个函数声明如下:

    namespace std{

         typedef void (*new_handler)();

         new_handler set_new_handler(new_handler p) throw();

    }

    其中,new_handler是个typedef,定义一个函数指针,该函数没有参数,也没有返回值;

    set_new_handler用于设置处理函数,设置p为当前处理函数,并返回之前的new_handler

    当operator new无法满足内存分配需求时,它会不断调用new_handler函数,直到找到足够的内存。

    因此,应该妥善设计new_handler函数,一个设计良好的new_handler必须做以下事情:

    1、删除其它无用的内存,使系统具有可以更多的内存可以使用,为下一步的内存申请作准备。

    实现此策略的办法是:程序一开始执行就分配一大块内存,当new_handler被调用时,将它们释放还给程序使用。

    2、设置另外一个new_handler。

    如果当前的new_handler不能够做到更多的内存申请操作,或者它知道另外一个new_handler可以做到,

    则可以调用set_new_handler函数设置另外一个new_handler,这样在operator new下一次调用的时候,

    可以使用这个新的new_handler。

    3、卸载new_handler,使operator new在下一次调用的时候,因为new_handler为空抛出内存申请异常。

    4、new_handler抛出自定义的异常

    5、不再返回,调用abort或者exit退出程序


    c++并不支持专属某一类的new_handler,但是如果需要,可以重载operator new,自己实现这个行为。

    只需为class提供自己的set_new_handler和operator new即可。

    在operator new中做如下事情:

    1、首先调用标准的set_new_handler,自定义专属类的处理函数

    2、调用global operator new,执行实际的内存分配。如果内存分配失败,刚才被安装的new_handler将被调用。

    3、无论new成功还是失败,都必须在类自定义的operator new结束前恢复全局new_handler

    这一部分的详细示例参考《effective c++》 条款7、8


    当内存分配请求不能满足时,调用你预先指定的一个出错处理函数。这个方法基于一个常规,即当operator new不能满足请求时,会在抛出异常之前调用客户指定的一个出错处理函数:一般称为new-handler函数。

    指定出错处理函数时要用到set_new_handler函数,它在头文件<new>里大致是象下面这样定义的:

    typedef void (*new_handler)();
    new_handler set_new_handler(new_handler p) throw();
    

    可以看到,new_handler是一个自定义的函数指针类型,它指向一个没有输入参数也没有返回值的函数。set_new_handler则是一个输入并返回new_handler类型的函数。

    set_new_handler的输入参数是operator new分配内存失败时要调用的出错处理函数的指针,返回值是set_new_handler没调用之前就已经在起作用的旧的出错处理函数的指针。

    operator new不能满足内存分配请求时,new-handler函数不只调用一次,而是不断重复,直至找到足够的内存。实现重复调用的代码在条款8里可以看到,这里我用描述性的的语言来说明:一个设计得好的new-handler函数必须实现下面功能中的一种。    

    ·产生更多的可用内存。这将使operator new下一次分配内存的尝试有可能获得成功。实施这一策略的一个方法是:在程序启动时分配一个大的内存块,然后在第一次调用new-handler时释放。释放时伴随着一些对用户的警告信息,如内存数量太少,下次请求可能会失败,除非又有更多的可用空间。

    ·安装另一个不同的new-handler函数。如果当前的new-handler函数不能产生更多的可用内存,可能它会知道另一个new-handler函数可以提供更多的资源。这样的话,当前的new-handler可以安装另一个new-handler来取代它(通过调用set_new_handler)。下一次operator new调用new-handler时,会使用最近安装的那个。(这一策略的另一个变通办法是让new-handler可以改变它自己的运行行为,那么下次调用时,它将做不同的事。方法是使new-handler可以修改那些影响它自身行为的静态或全局数据。)   

    ·卸除new-handler。也就是传递空指针给set_new_handler。没有安装new-handler,operator new分配内存不成功时就会抛出一个标准的std::bad_alloc类型的异常。  

    ·抛出std::bad_alloc或从std::bad_alloc继承的其他类型的异常。这样的异常不会被operator new捕捉,所以它们会被送到最初进行内存请求的地方。(抛出别的不同类型的异常会违反operator new异常规范。规范中的缺省行为是调用abort,所以new-handler要抛出一个异常时,一定要确信它是从std::bad_alloc继承来的。想更多地了解异常规范,参见条款m14。)

    ·没有返回。典型做法是调用abort或exit。abort/exit可以在标准c库中找到(还有标准c++库,参见条款49)。

    c++不支持专门针对于类的new-handler函数,而且也不需要。你可以自己来实现它,只要在每个类中提供自己版本的set_new_handler和operator new。类的set_new_handler可以为类指定new-handler(就象标准的set_new_handler指定全局new-handler一样)。类的operator new则保证为类的对象分配内存时用类的new-handler取代全局new-handler。

    假设处理类x内存分配失败的情况。因为operator new对类型x的对象分配内存失败时,每次都必须调用出错处理函数,所以要在类里声明一个new_handler类型的静态成员。那么类x看起来会象这样:

    class x {
    public:
    	static new_handler set_new_handler(new_handler p);
    	static void * operator new(size_t size);
    	
    private:
    	static new_handler currenthandler;
    };
    

    类的静态成员必须在类外定义。因为想借用静态对象的缺省初始化值0,所以定义x::currenthandler时没有去初始化。

    new_handler x::currenthandler;	//缺省设置currenthandler为0(即null)

    类x中的set_new_handler函数会保存传给它的任何指针,并返回在调用它之前所保存的任何指针。这正是标准版本的set_new_handler所做的:

    new_handler x::set_new_handler(new_handler p)
    {
    	new_handler oldhandler = currenthandler;
    	currenthandler = p;
    	return oldhandler;
    }

    最后看看x的operator new所做的:

    1. 调用标准set_new_handler函数,输入参数为x的出错处理函数。这使得x的new-handler函数成为全局new-handler函数。注意下面的代码中,用了"::"符号显式地引用std空间(标准set_new_handler函数就存在于std空间)。

    2. 调用全局operator new分配内存。如果第一次分配失败,全局operator new会调用x的new-handler,因为它刚刚(见1.)被安装成为全局new-handler。如果全局operator new最终未能分配到内存,它抛出std::bad_alloc异常,x的operator new会捕捉到它。x的operator new然后恢复最初被取代的全局new-handler函数,最后以抛出异常返回。

    3. 假设全局operator new为类型x的对象分配内存成功,, x的operator new会再次调用标准set_new_handler来恢复最初的全局出错处理函数。最后返回分配成功的内存的指针。 c++是这么做的:

    void * x::operator new(size_t size)
    {
    	new_handler globalhandler =		// 安装x的new_handler     
    		std::set_new_handler(currenthandler);
    		
    void *memory;
    
    try { 	// 尝试分配内存
    	memory = ::operator new(size);
    } 
    
    catch (std::bad_alloc&) { 	// 恢复旧的new_handler
    	std::set_new_handler(globalhandler);      
    	throw;	// 抛出异常
    }
    	std::set_new_handler(globalhandler);	// 恢复旧的new_handler
    	return memory;
    }

    使用类x的内存分配处理功能时大致如下:

    void nomorememory();// x的对象分配内存失败时调用的new_handler函数的声明
    
    x::set_new_handler(nomorememory);
    		// 把nomorememory设置为x的
    		// new-handling函数
    x *px1 = new x;
    		// 如内存分配失败,
    		// 调用nomorememory
    string *ps = new string;
    		// 如内存分配失败,调用全局new-handling函数		
    x::set_new_handler(0);
    		// 设x的new-handling函数为空
    x *px2 = new x;
    		// 如内存分配失败,立即抛出异常
    		// (类x没有new-handling函数)
    

    你会注意到,处理以上类似情况,如果不考虑类的话,实现代码是一样的,这就很自然地想到在别的地方也能重用它们。正如条款41所说明的,继承和模板可以用来设计可重用代码。在这里,我们把两种方法结合起来使用,从而满足了你的要求。

    template<class t>	// 提供类set_new_handler支持的
    class newhandlersupport {	// 混合风格”的基类
    public:
    	static new_handler set_new_handler(new_handler p);
    	static void * operator new(size_t size);
    
    private:
    	static new_handler currenthandler;
    };
    
    template<class t>
    new_handler newhandlersupport<t>::set_new_handler(new_handler p)
    {
    	new_handler oldhandler = currenthandler;
    	currenthandler = p;
    	return oldhandler;
    }
    template<class t>
    void * newhandlersupport<t>::operator new(size_t size)
    {
    	new_handler globalhandler =
    		std::set_new_handler(currenthandler);
    	void *memory;
    	try {
    		memory = ::operator new(size);
    	}
    	catch (std::bad_alloc&) {
    		std::set_new_handler(globalhandler);
    		throw;
    	}
    	
    	std::set_new_handler(globalhandler);
    	return memory;
    }
    // this sets each currenthandler to 0
    
    template<class t>
    new_handler newhandlersupport<t>::currenthandler;
     有了这个模板类,对类x加上set_new_handler功能就很简单了:只要让x从newhandlersupport<x>继承:
    // note inheritance from mixin base class template. (see
    // my article on counting objects for information on why
    // private inheritance might be preferable here.)
    class x: public newhandlersupport<x> {
    
    ...		// as before, but no declarations for
    };		// set_new_handler or operator new
    
  • 相关阅读:
    Android OpenGL 编写简单滤镜
    linux 文件系统
    此博客不再更新
    golang sync包
    KADEMLIA算法
    golang 类型转换
    golang 小例子
    go-ehtereum编译:
    golang编译库文件方式
    以太坊(Ethereum)智能合约NodeJS/Web3 使用
  • 原文地址:https://www.cnblogs.com/ljygoodgoodstudydaydayup/p/4209879.html
Copyright © 2011-2022 走看看