• new内存分配失败


    我们都知道,使用 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 ";

    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++库)。

     

    上面的选择给了你实现new-handler函数极大的灵活性。

    更多知识请参考《Effective c++》

  • 相关阅读:
    java基本数据类型及运算的注意事项
    B-Tree 和 B+Tree 结构及应用,InnoDB 引擎, MyISAM 引擎
    软件工程与软件开发模型、软件开发方法
    2020年3月份Unity3D游戏源码合集-免费下载
    独立游戏开发必备!8个效果不错的Unity3D 免费模型资源包
    2019年4月份整理的Unity3D 20个实用插件-免费下载
    2019年4月份整理的Unity3D游戏完整源码
    2019年3月整理的2D美术资源合集
    画面效果都不错!20个精品Unity3D着色器插件
    Unity3D中UnityPlayerActivity与UnityPlayerNativeActivity有什么区别
  • 原文地址:https://www.cnblogs.com/aibox222/p/9479695.html
Copyright © 2020-2023  润新知