转自: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
https://blog.csdn.net/gudongxian/article/details/48196499

处理new分配内存失败情况的更多相关文章

  1. C++内存管理5-处理new分配内存失败情况(转)

    C++内存管理5-处理new分配内存失败情况(转) endl; 参考博客: https://www.cnblogs.com/findumars/p/9905195.html

  2. C++new失败的处理(如果 new 分配内存失败,默认是抛出异常的,但也可以取消异常)

    我们都知道,使用 malloc/calloc 等分配内存的函数时,一定要检查其返回值是否为“空指针”(亦即检查分配内存的操作是否成功),这是良好的编程习惯,也是编写可靠程序所必需的.但是,如果你简单地 ...

  3. malloc函数分配内存失败的常见原因

    malloc()函数分配内存失败的常见原因:  1. 内存不足.  2. 在前面的程序中出现了内存的越界访问,导致malloc()分配函数所涉及的一些信息被破坏.下次再使用malloc()函数申请内存 ...

  4. 在内存充足时malloc函数分配内存失败的原因及解决

    昨天在修改自己的代码的时候,碰到了malloc函数内存分配失败,上网翻了翻,一个很可能的原因是之前的代码出现了越界操作,导致malloc分配函数所涉及的一些信息被破坏.在这个思想的指导下,今天又是郁闷 ...

  5. Unix系统编程()在堆上分配内存

    在堆上分配内存:malloc和free 一般情况下,C程序使用malloc函数族在堆上分配和释放内存.较之brk和sbrk,这些函数具备不少优点: 属于C语言标准的一部分 更易于在多线程程序中使用 接 ...

  6. 浅析C++内存分配与释放操作过程——三种方式可以分配内存new operator, operator new,placement new

    引言:C++中总共有三种方式可以分配内存,new operator, operator new,placement new. 一,new operator 这就是我们最常使用的 new 操作符.查看汇 ...

  7. du: fts_read 失败: 无法分配内存

    今天在查看一个大的文件时突然报出一个du: fts_read 失败: 无法分配内存的错误. 用 ulimit -a 查看下 core file size (blocks, -c) 0 data seg ...

  8. linux设备驱动归纳总结(五):1.在内核空间分配内存【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-79134.html linux设备驱动归纳总结(五):1.在内核空间分配内存 xxxxxxxxxxxx ...

  9. c语言中-----分配内存函数

    原型: void * realloc(void *p, size_t  size); realloc 可以对给定的指针所指的空间进行扩大 或者 缩小, 原有内存的数据保持不变.当然,对于缩小,则缩小部 ...

随机推荐

  1. os下,vs code 自动编译ts

    nodejs安装ts npm install -g typescript 进入工程目录,用命令初始化ts(生成tsconfig.json) tsc --init 如果要指定生成目录,打开tsconfi ...

  2. CAD多个点构造选择集(网页版)

    主要用到函数说明: IMxDrawSelectionSet::SelectByPolygon 在多个点组合的闭合区域里,构造选择集.详细说明如下: 参数 说明 [in] IMxDrawPoints* ...

  3. CAD控件:控件图形数据库概要说明

    1.1 控件数据库 3 1.1.1 数据库概述 3 1.2 数据库初始化 4 1.3 创建和组织数据库 4 1.4 保存数据库 4 1.5 插入一个数据库 4 1.6 设置当前数据库值 5 1.6.1 ...

  4. maven引入的包无法使用 解决方法

    如果正常引入后在依赖中能够找到包,但是打不开 1.有可能是包下载不完整 把maven下载源由国外转成阿里镜像源找到 maven 的配置文件: settings.xml 文件: <mirror&g ...

  5. 07C语言程序语句

    C语言程序语句 判断语句 if(表达式) {语句} #include <stdio.h> int main(){ printf("请输入2个数字:"); int a,b ...

  6. 易忘小技巧--yum

    写在前面:日常维护系统中,如果不是天天接触,难免会忘记一些实用小技巧,本文记录自己使用的小技巧,有新发现时,会长期更新 安装epel和dnf源 # 安装epel yum -y install epel ...

  7. 牛客多校Round 5

    Solved:3 rank:195 F. take 官方题解:小 A 在打开第 i 个箱子后会交换手中的钻石和第 i 个箱子中的钻石 当且仅当第 i个箱子的钻石是前 i 个箱子打开后出现的所有钻石里最 ...

  8. C#服务端通过Socket推送数据到Android端App中

    需求: 描述:实时在客户端上获取到哪些款需要补货. 要求: 后台需要使用c#,并且哪些需要补货的逻辑写在公司框架内,客户端采用PDA(即Android客户端 版本4.4) . 用户打开了补货通知页面时 ...

  9. Address already in use: JVM_Bind:8080错误的解决办法

    解决办法:先到命令行查找8080端口号被那个占用,输入后面的命令:netstat -ano 查到 然后到任务管理器把PID为20904的进程给结束掉,就OK了 -------------------- ...

  10. CTSC2018 Day2T1 Juice混合果汁

    [题解] 在考场上A掉的题. 把美味度排个序,然后按照价格p为权值建立主席树,把每个果汁按照拍好的顺序添加进去.主席树上维护总升数cnt以及总价格sum.对于每个询问,我们二分一个美味值,check的 ...