C++ 处理new分配内存失败

转自:http://www.51testing.com/html/70/n-827070.html

在C++语言中,我们经常会使用new给一个对象分配内存空间,而当内存不够会出现内存不足的情况。C++提供了两中报告方式:

  1、抛出bad_alloc异常来报告分配失败;

  2、返回空指针,而不会抛出异常。

  C++为什么会采用这两种方式呢?这主要是由于各大编译器公司设计C++编译器公司的结果,因为标准C++是提供了异常机制的。例如,VC++6.0中当new分配内存失败时会返回空指针,而不会抛出异常。而gcc的编译器对于C++标准支持比较好,所以当new分配内存失败时会抛出异常。

  究竟为什么会出现这种情况呢?

  首先,C++是在C语言的基础之上发展而来,而且C++发明时是想尽可能的与C语言兼容。而C语言是一种没有异常机制的语言,所以C++应该会提供一种没有异常机制的new分配内存失败报告机制;(确实是如此,早期的C++还没有加入异常机制)

  其次在返回空指针的实现过程中,C++采用的是malloc/calloc 等分配内存的函数,该类函数不会抛出异常,但是在分配内存失败时会返回“空指针”。

  最后,对于标准的C++,有着比较完善的异常处理机制,所以对于出现异常时,会抛出响应的异常。对于new分配失败时,系统会抛出bad_alloc异常。

  鉴于以上原因,我们在不同的编译器需要new分配失败时做不同的处理。例如:

  情况1:

int* p = new int(5);
if ( p == 0 ) // 检查 p 是否空指针
    return -1;
...

  情况2:

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

  情况1和情况2的代码都是对于new失败时的处理,而针对不同的编译器,可以这种处理会完全失效。如果在gcc编译器采用情况1,那么if(p==0)完全是没有意义的,因为不管new内存分配成功失败与否,都不会出现p=0的情况。即,如果分配成功,p=0完全不可能;而分配失败,new会抛出异常跳过其后面的代码。而需要采用情况2的处理方式,即应该来捕捉异常。

  同样,如果在VC++6.0中采用情况2的代码,那么new分配失败时,完全不会抛出异常,那么捕捉异常也是徒劳的。

  所以在new分配内存的异常处理时要特别小心,可以两种方式联合使用,来解决跨平台跨编译器的难题。

  当然情况2中的异常处理代码是最简单的处理方式,下面我们为其制定一个客户制定的错误处理函数,即new-handler。

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


这里首先定义new-handler函数指针类型,然后定义一个new-handler函数set_new_handler,其参数是指向operator new无法分配足够内存时应该被调用的函数。其返回指也是一个指针,指向set_new_handler被调用前正在执行(但是马上就要被替换)的那个new-handler函数。下面设计一个当operator new无法分配足够内存时应该被调用的函数:

void noMemoryToAlloc()
{
       std::cerr << "unable to satisfy request for memory\n";

        std::abort();
}

  使用noMemoryToAlloc函数的代码为:

int main()
{
       set_new_handler(nomorememory);
       int *pArray = new int[100000000000000L];

...

}

  当operator new无法分配足够空间时,noMemoryToAlloc就会被调用,于是程序就会发出一个错误信息cerr之后,调用abort函数结束程序。

  如果operator new无法分配足够空间时,我们希望不断调用new-handler函数,直到找到足够内存为止,那么我们的operator new函数就可以设计为:

void *operator new(std::size_t size) throw(std::bad_alloc)
{
        if ( size==0 ) {
             size  = 1;
        }
       while (true) {
            调用malloc等内存分配函数来尝试分配size大小的内存;
            if ( 分配成功 )
                 return 指向分配得来的内存指针;
            new_handler globalHandler  = set_new_handler(0);
            set_new_handle(globalHandler);
            if(globalHandler)
                   (*globalHandler)();
           else
                   throw std::bad_alloc();
       }
}


转自:http://blog.sina.com.cn/s/blog_9f1c09310101953s.html


 使用new分配内存失败时往往会使用asert()终止程序,但是这只能在除错模式下abert函数才能有效,在生产模式下,abert只是一个void指令,所以连程序都跳不出来。

      而当new操作失败时,一个好的程序不能简单的终止程序就行了,而是要尝试去释放内存
      如何能在new操作失败,在抛出异常之前先把相应的处理做了呢?这就要用到new_handler了,它是在抛出exception调用的。为了指定这个所谓的“内存不足处理函数,new_handler”,client必须调用set_new_handler,这是头文件提供的函数,用法:
      typedef void (*new_handler)()
      new_handler set_new_handler(new_handler p) throw();
简单的程序测试:
#include "stdafx.h"
#include
#include
using namespace std;

void NoMoreMemory()
{
cout<<"Unable to statisty request for memory"<<endl;
abort();
}

int _tmain(int argc, _TCHAR* argv[])
{
set_new_handler(NoMoreMemory);
int *p=new int[536870911];
return 0;
}
注:当operator new 无法满足内存需求时,它会不只一次地调用new_handler函数(如果new_handler没有退出程序的话);它会不断地调用,直到找到足够的内存为止。因此一个良好设计的new_handler函数必须完成下列事情之一:
1)让更多的内存可用。这或许能够让operator new 的下一次内存配置行为成功。实现此策略的方法之一就是在程序起始时配置一大块内存,然后在new_handler第一次被调用时释放之。如此的释放动作常常伴随着某种警告信息,告诉用户目前的内存已经处于低水位,再来的内存需求可能失败,除非有更多的内存回复自由身。
2)安装一个不同的new_handler,如果目前的new_handler无法让更多的内存可用,或许它知道另一个new_handler手上我有比较多的资源。果真如此,目前的new_handler就可以安装另外一个new_handler以取代自己(只要再调用一次set_new_handler即可)。当operator new下次调用new_handler函数时,它会调用最新安装的那个。
3)卸除掉这个new_handler,也就是说,将null指针传给set_new_handler,一旦没有安装任何new_handler,operator new就会在内存配置失败时抛出一个类型为std::bad_alloc的exception。
4)抛出一个exception,类型为std::bad_alloc(或者派生类型)。这样的exception不会被operator new捕获,所以它们就会传动到最初踢出内存需求的那个点上
5)不返回,直接调用abort或exit

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值