美文网首页收集一些技术好文
关于 C++ new一些失败情况与处理

关于 C++ new一些失败情况与处理

作者: JoeJiang | 来源:发表于2017-07-23 10:41 被阅读76次

    Copyright @Copy+Joel at 2017.07.23 08:00 a.m in Shenzhen.China. LIST- TE- E11 -03

    使用 malloc/calloc 等分配内存的函数时,一定要检查其返回值是否为“空指针”(亦即检查分配内存的操作是否成功),这是良好的编程习惯,也是编写可靠程序所必需的。但是,如果你简单地把这一招应用到 new 上,那可就不一定正确了。我经常看到类似这样的代码:

            int* p = new int[SIZE];        if ( p == 0 )```
     // 检查 p 是否空指针            return -1;        // ....
    
    其实,这里的 if ( p == 0 ) 完全是没啥意义的。C++ 里,如果 new 分配内存失败,默认是**抛出异常**的。所以,如果分配成功,p == 0 就绝对不会成立;而如果分配失败了,也不会执行 if ( p == 0 ),因为分配失败时,new 就会**抛出异常跳过后面的代码**。如果你想检查 new 是否成功,应该**捕捉异常**:
    

    try { int* p = new int[SIZE]; // 其它代码 } catch ( const bad_alloc& e ) { return -1; }

    据说一些老的编译器里,new 如果分配内存失败,是不抛出异常的(大概是因为那时 C++ 还没加入异常机制),而是和 malloc 一样,返回空指针。不过我从来都没遇到过 new 返回空指针的情况. 当然,标准 C++ 亦提供了一个方法来**抑制 new 抛出异常**,而返回空指针:
    
    

    int* p = new (std::nothrow) int;
    // 这样如果 new 失败了,就不会抛出异常,而是返回空指针
    if ( p == 0 ) // 如此这般,这个判断就有意义了
    return -1;

    
    详解:
    首先按c++标准的话,new失败会抛出bad_alloc异常,但是有些编译器对c++标准支持不是很好,比如vc++6.0中new失败不会抛出异常,而返回0.
    //不支持c++标准的做法如下
    

    double *ptr=new double[1000000];
    if( 0 == ptr)
    ……处理失败……

    //标准推荐做法一:
    

    try
    {
    double *ptr=new double[1000000];
    }
    catch(bad_alloc &memExp)
    {
    //失败以后,要么abort要么重分配
    cerr<<memExp.what()<<endl;
    }

    //标准推荐做法二:
    是使用set_new_handler函数处理new失败。它在头文件<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没调用之前就已经在起作用的旧的出错处理函数的指针。
    可以象下面这样使用set_new_handler:
    

    // function to call if operator new can't allocate enough memory
    void nomorememory()
    {
    cerr << "unable to satisfy request for memory\n";
    abort();
    }
    int main(){
    set_new_handler(nomorememory);
    int *pbigdataarray = new int[100000000];
    ...
    }

    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继承来的。想更多地了解异常规范)
    * 没有返回。典型做法是调用abort或exit。abort/exit可以在标准c库中找到(还有标准c++库)。
    
    ***
    The sharing of knowledge, the spirit of encouragement.
    By Joel Jiang (江东敏)

    相关文章

      网友评论

        本文标题:关于 C++ new一些失败情况与处理

        本文链接:https://www.haomeiwen.com/subject/siwpkxtx.html