Effective C++ 条款49

了解new-handler的行为

本节条款讲述的技术是,在operator new抛出异常以前,会先调用一个客户指定的错误处理函数:new-handler。当内存分配失败的时候如何自定义并使用这个内存异常处理函数。关键语句就是set_new_handler。作者重点强调如何对于class的内存分配,实现不同的内存分配错误处理函数。

我们先来举个书上的例子,先弄明白如何使用new-handler技术。

如下代码:

 void outOfMem()
    {
        std::cerr<<"Unable to satisfy request for memoryn";
        std::abort();//强制终止所在进程
    }
    int main()
    {
        std::set_new_handler(outOfMem);
        int *pBigDataArray=new int[100000000L];
       return 0;
    }

如上代码,因为调用了set_new_handler(outOfMem);,如果内存分配失败,则会转到outOfMen函数继续执行。这样我们就萌发出对于我们自己定义的不同类,能不能实现不同类的内存分配问题用不同的函数进行处理的想法。比如说类A有类A的自定义内存处理函数,类B有类B的自定义内存处理函数,为了实现这种功能,作者提出了以下实现方法。

不过顺带提一下,我们定义的内存处理函数要有以下特点:

1.让更多内存可被使用。这样可以造成operator new内的下一次内存分配动作可能成功。一个做法是,程序一开始就分配一大块内存,当new-handler第一次被调用时将它释放。

2.安装另一个new-handler。当前的new-handler无法取得更多内存时,或许它直到哪个new-handler有此能力。

3.卸除new-handler。即将null指针传给set_new_handler,一旦没有安装任何new-handler,operator new在内存分配不成功时便抛出异常。

4.抛出bad_alloc(或派生自bad_alloc)的异常。这样的异常不会被operator new捕捉,因此不会被传播到内存索求处。

5.不返回。通常abort或exit。

  class NewHandlerHolder{
    public:
        explicit NewHandlerHolder(std::new_handler nh)
        :handlere(nh){}
        ~NewHandlerHolder()
        { std::set_new_handler(handler); }
    private:
        std::new_handler handler;
        NewHandlerHolder&(const NewHandlerHolder&);//防止copying
        NewHandlerHolder& operator-(const NewHandlerHolder&);
    };

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 currentHandler;
    };
    std::new_handler Widget::currentHandler=0;
    std::new_handler Widget::set_new_handler(std::new_handler p) throw()
    {
        std::new_handler oldHandler=currentHandler;
        currentHandler=p;
        reutrn oldHandler;
    }

void* Widget::operator new(std::size_t size) throw(std::bad_alloc)
    {
        NewHandlerHolder h(std::set_new_handler(currentHandler));//安装Widget的new-handler
        return ::operator new(size);
    }

    void outOfMem();
    Widget::set_new_handler(outOfMem);//设定outOfmem为Widget的new-handling函数
    Widget* pw1=new Widget;//内存分配失败,则调用outOfMEM
    std::string* ps=new std::string;//内存分配失败则调用global new-handling(如果有)
    Widget::set_new_handler(0);//设定Widget专属new-handling为null
    Widget* pw2=new Widget;//内存分配失败则立刻抛出异常

我来说明一下,以上程序,对于定义class NewHandlerHolder,是为了保存替换前的内存处理函数,我们来跟踪一下以上代码的执行过程。首先,声明一个outOfMem(),接着通过Widget::set_new_handler(outOfMem)语句设置Widget内的static std::new_handler currentHandler变量。然后,调用new Widget时,我们进入Widget::operator new(std::size_t size) throw(std::bad_alloc)函数体内,NewHandlerHolder h(std::set_new_handler(currentHandler));语句起到的作用如下,1.在定义一个NewHandlerHolder对象的时候将替换前的内存处理函数保存在该对象的std::new_handler handler变量中,2.设置新的针对类Widget的new_handler函数。执行::operator new(size)进行内存分配,如果内存分配出错则调用刚替换的内存处理函数。在退出该函数体的时候,因为NewHandlerHolder的析构函数的,还原内存处理函数,不影响以后别的内存分配异常的执行。

以上版本实现了我们的需求功能。

然而,作者又提出了另一种利用模板实现的方法:

  class NewHandlerHolder{
    public:
        explicit NewHandlerHolder(std::new_handler nh)
        :handlere(nh){}
        ~NewHandlerHolder()
        { std::set_new_handler(handler); }
    private:
        std::new_handler handler;
        NewHandlerHolder&(const NewHandlerHolder&);//防止copying
        NewHandlerHolder& operator-(const NewHandlerHolder&);
    };
 template<typename T>
    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 currentHandler;
    };

    template<typename T> std::new_handler
    NewHandlerSupport<T>::set_new_handler(std::new_handler p) 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 ::operator new(size);

    template<typename T>
    std::new_handler NewHandlerSupport<T>::currentHandler=0;

    class Widget:public NewHandlerSupport<Widget>{

    };

这种实现方式对我来说,暂时感觉别扭,可是它却体现出模板潜在的编程价值。当我们调用语句class Widget:public NewHandlerSupport<Widget>时,我们实例化了一个NewHandlerSupport<Widget>类,我们的类Widget继承自该类,所以widget类中继承了NewHandlerSupport的函数,实际程序执行过程和第一种方式一样,唯一不同的地方就是类widget不用重新定义有关内存处理的静态函数。

时间: 2024-10-14 00:43:30

Effective C++ 条款49的相关文章

Effective C++ 条款49 了解new handler的行为

1. 当operator new无法满足某一内存分配需求时,它会先调用一个客户指定的错误处理函数(如果客户未指定,它就会抛出异常),所谓的new-handler.为了指定这个"用以处理内存不足"的函数,客户必须调用set_new_handler,那是声明于<new>的一个标准库函数: namespace std{ typedef void (*new_handler)(); new_handler set_new_handler(new_handler p) throw()

《Effective C++》:条款49:了解new-handler的行为

C++内存是由程序员手动管理的,不像Java或.net有垃圾回收机制.C++内存管理主要是分配例程和归还例程(allocation and deallocation routines),即operator new和operator delete,还有一个配合的角色new-handler.当涉及到数组时,上面提到的operator new和operator delete就会变为operator new[]和operator delete[]. 内存管理在多线程环境下更为复杂,因为heap是一个可被

More Effective C++ 条款35 让自己习惯于标准C++ 语言

(由于本书出版于1996年,因此当时的新特性现在来说可能已经习以为常,但现在重新了解反而会起到了解C++变迁的作用) 1. 1990年后C++的重要改变 1). 增加了新的语言特性:RTTI,namespaces,bool,关键词mutable和explicit,enums作为重载函数之自变量所引发的类型晋升转换,以及"在class 定义区内直接为整数型(intergral) const static class members设定初值"的能力. 2). 扩充了Templates的特性

effective c++ 条款4 make sure that objects are initialized before they are used

1 c++ 类的数据成员的初始化发生在构造函数前 class InitialData { public: int data1; int data2; InitialData(int a, int b) { data1 = a: //this is assignment data2 = b; //this is assignment } /* InitialData(int a, int b):data1(a),data2(b) //this is initial {} */ } 2 不同cpp文

More Effective C++ 条款34 如何在一个程序中结合C++和C

1. C++和C混合使用的前提之一就是编译器产生兼容的目标文件(.lib和.dll等).所谓"兼容",指的是编译器在"预编译器相依的特性上"一致,如int和double大小,参数压栈机制等,只有在这个基础上才能讨论结合使用C++和C模块的问题. 2. 在1的基础上,要结合使用C++和C的模块,主要有以下几点需要注意: 1). name mangling(名称重整) Name mangling是C++用于支持函数重载的机制,它对重载的函数名称进行一定改变,使得每个函数

Effective C++ 条款3 尽可能用const

1. const可被施加于任何作用域内的对象,函数参数,函数返回类型,成员函数本体.用const修饰指针,如果const出现在*之前,表明指针不能更改所指向的对象的内容,如果const出现在*之后,表明指针只能指向同一块内存.另外int const*p和const int*p含义相同.如果对象成员有普通指针,那么构造该类的一个const对象时,const修饰使得该指针只能指向同一块内存,但指针指向的内容可以改变. 2. 将某些东西声明为const可以帮助编译器侦测出错误用法. 3. 编译器强制实

STL笔记(5)条款49:学习破解有关STL的编译器诊断信息

STL笔记(5)条款49:学习破解有关STL的编译器诊断信息 条款49:学习破解有关STL的编译器诊断信息 用一个特定的大小定义一个vector是完全合法的, vector<int> v(10);    // 建立一个大小为10的vector 而string在很多方面像vector,所以你可能希望可以这么做: string s(10);        // 常识建立一个大小为10的string 这不能编译.string没有带有一个int实参的构造函数.我的一个STL平台像这样告诉我那一点: e

effective c++ 条款13 use object to manage resources.

请求的系统资源需要最终还回系统,为了避免遗忘返还这个动作,可以利用析构函数在object销毁时自动调用的特点来实现. 简单说就是用object来管理资源. 以内存资源为例 class Investment {}; Investment* creatInvestment(){...} // factory function to produce investment object void main() { Investment* pInv = creatInvestment();//call t

effective c++ 条款3 use const whereever you can

1 const 传达的意思应该是这个变量是常量不能更改 2 const 在 * 左边表示数据是const,在右边表示指针是const // char greeting[] = "hello"; char* p = greeting; //const *: const data //* const: const pointer char const *p1 = greeting; // const data char * const p2 = greeting; // const poi