C++new失败的处理

来源:互联网 发布:方正字体包 mac 编辑:程序博客网 时间:2024/05/29 19:39

我们都知道,使用 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++ 库 ) 。

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

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

0 0
原创粉丝点击