如何在C++中处理bad_alloc?

Nos*_*tap 51 c++ memory-management bad-alloc

有一种方法调用foo有时会返回以下错误:

terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
Abort
Run Code Online (Sandbox Code Playgroud)

有没有我可以用一种方式try- catch块从终止我的程序停止这个错误(所有我想要做的就是回报-1)?

如果是这样,它的语法是什么?

我还能用bad_allocC++ 处理什么?

Kon*_*lph 79

一般来说,你不能不应该试图对这一错误作出回应.bad_alloc表示无法分配资源,因为没有足够的可用内存.在大多数情况下,您的程序无法应对这种情况,并且很快就会终止是唯一有意义的行为.

更糟糕的是,现代操作系统经常过度分配:malloc并且new总是返回一个有效的指针,即使技术上没有(或没有足够的)可用内存 - 所以std::bad_alloc永远不会抛出,或者至少不是内存耗尽的可靠标志.相反,尝试访问分配的内存将导致错误,这是不可捕获的.

捕获时唯一可以做的std::bad_alloc就是记录错误,并尝试通过释放未完成的资源来确保安全的程序终止(但是如果程序使用RAII,则会在错误被抛出后的正常堆栈展开过程中自动完成适当地).

在某些情况下,程序可能会尝试释放一些内存并重试,或者使用辅助内存(=磁盘)而不是RAM,但这些机会仅存在于非常特定的场景中.

  • @KonradRudolph,可能有点太多说"不能也不应该尝试"来回应这个错误.如果该程序位于敏感的主机(核工厂,机器人,火星车)中,那么只能让自己被异常杀死是不可接受的呢?如果你_could_实际处理它会怎么样?更简单的例子是加载对象的游戏.如果没有足够的内存,它可以尝试使用质量较低的网格重新加载,或者减少粒子的数量,或者显示丢失的任何对象的立方体等. (10认同)
  • @SHahbaz,人们可以随时采取Matlab过去的路线.假设用户无意中要求几乎所有虚拟内存.知道了机器的大小,Matlab认为"我能做到这一点!但这些事情必须做得很精致*!" 它并没有同时满足所有记忆.它反过来一点一点地进行.在失败时,它进入一个繁忙的循环并一次又一次地重试.当其他一些进程退出时,Matlab尽快吞噬了那个内存.最终,除了重新启动之外,没有什么可以留下Matlab的野兽了.捕获分配失败并不是一个好主意. (8认同)
  • @DavidHammen,这是真的。实际上它无论如何都可能用 C 编写,因为 C++ 的许多特性对于系统编程来说太高级了,而且比 C 更不可控。很好被杀死”是可以的,但是说“你**不应该**检查 bad_alloc”太多了。您无法想象每个用例。对于某些人来说,也许检查 bad_alloc 实际上很有用。 (4认同)
  • @KonradRudolph:"无论如何你都不会从中得到有意义的诊断,因为大多数现代系统都不会让malloc失败,他们只是将不存在的内存标记为保留." - 只是一个想法,但也许这个评论应该编辑成答案本身,因为它是一个关键的洞察力,为什么处理'bad_alloc`的尝试可能是无用的或不可靠的.... (3认同)
  • “大多数现代系统永远不会使malloc失败”-事实并非如此。在Windows上,Malloc和new肯定会失败,而Windows仍是2015年最广泛使用的操作系统。不幸的是,支持32位应用程序对于大多数供应商仍然很重要,并且虚拟地址空间不足或内存碎片是常见的问题。(顺便说一句,新的Windows上的64位应用程序也会失败。) (3认同)
  • @BasileStarynkevitch问题不是如何捕获错误,而是如何继续.如果这个循环是你的应用程序的核心,你所做的一切取决于它的结果,没有它就很难继续.如果你没有足够的记忆,你可以保留,但可以免费,再次尝试几乎没用.如果你不能再试一次,那么抓住它几乎没用.您可以打印出错误消息并退出,但这应留给`main`或其他顶级代码,绝对不应该在库代码中发生. (2认同)
  • 事实上,我会说你应该在大多数情况下捕获并处理该异常,就像你检查 `malloc` 的输出不是 `NULL` 并处理它的方式一样。 (2认同)

Alo*_*ave 38

什么是C++标准new在c ++中指定的行为?

通常的想法是,如果new运算符不能分配所请求大小的动态内存,那么它应该抛出类型的异常std::bad_alloc.
但是,甚至在bad_alloc抛出异常之前会发生更多事情:

C++ 03第3.7.4.1.3节:

无法分配存储的分配函数可以调用当前安装的new_handler(18.4.2.2)(如果有).[注意:程序提供的分配函数可以使用set_new_handler函数(18.4.2.3)获取当前安装的new_handler的地址.]如果使用空异常规范(15.4)声明的分配函数throw(),则无法分配存储,它应返回一个空指针.任何其他无法分配存储的分配函数都只能通过抛出类std :: bad_alloc(18.4.2.1)或从std :: bad_alloc派生的类的异常来指示失败.

请考虑以下代码示例:

#include <iostream>
#include <cstdlib>

// function to call if operator new can't allocate enough memory or error arises
void outOfMemHandler()
{
    std::cerr << "Unable to satisfy request for memory\n";

    std::abort();
}

int main()
{
    //set the new_handler
    std::set_new_handler(outOfMemHandler);

    //Request huge memory size, that will cause ::operator new to fail
    int *pBigDataArray = new int[100000000L];

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,operator new(很可能)将无法为100,000,000个整数分配空间,并且outOfMemHandler()将调用该函数,并且在发出错误消息后程序将中止.

如此处所示,new当无法满足内存请求时,操作符的默认行为是new-handler重复调用该函数,直到它找到足够的内存或者没有更多新的处理程序.在上面的例子中,除非我们调用std::abort(),outOfMemHandler()重复调用.因此,处理程序应该确保下一个分配成功,或者注册另一个处理程序,或者不注册处理程序,或者不返回(即终止程序).如果没有新的处理程序并且分配失败,则操作员将抛出异常.

什么是new_handlerset_new_handler

new_handler是一个指向函数的指针的typedef,该函数获取并不返回任何内容,并且set_new_handler是一个获取并返回a的函数new_handler.

就像是:

typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();
Run Code Online (Sandbox Code Playgroud)

set_new_handler的参数是一个指向函数运算符的指针,new如果它不能分配所请求的内存则应该调用.它的返回值是指向先前注册的处理函数的指针,如果没有先前的处理程序,则返回null.

如何处理C++中的内存不足情况?

鉴于new精心设计的用户程序的行为应该通过提供适当的操作new_handler来处理内存不足的情况,该操作执行以下操作之一:

提供更多可用内存:这可能允许运算符new循环内的下一次内存分配尝试成功.实现此目的的一种方法是在程序启动时分配大块内存,然后在第一次调用new-handler时释放它以在程序中使用.

安装一个不同的新处理程序:如果当前的新处理程序无法使更多的内存可用,并且还有另一个可以的新处理程序,那么当前的new-handler可以在其位置安装另一个new-handler(通过电话set_new_handler).下一次operator new调用new-handler函数时,它将获得最近安装的函数.

(这个主题的一个变体是新的处理程序来修改它自己的行为,所以下次调用它时,它会做一些不同的事情.实现这一点的一种方法是让new-handler修改static,namespace-specific,或者影响新处理程序行为的全局数据.)

卸载new-handler: 这是通过传入一个空指针来完成的set_new_handler.如果没有安装新处理程序,operator newstd::bad_alloc在内存分配不成功时会抛出异常((可转换为)).

抛出一个可兑换的例外std::bad_alloc.此类异常不会被捕获operator new,但会传播到发起内存请求的站点.

不归还:通过电话abortexit.

  • 这并没有直接回答Q问题的确切答案(Konrad的答案已经很好地回答了)但是这个答案提供了一个关于在引发`bad_alloc`之前在c ++中处理内存不足情况的不太常见的方法的见解. (5认同)

Fle*_*exo 31

您可以像任何其他异常一样捕获它:

try {
  foo();
}
catch (const std::bad_alloc&) {
  return -1;
}
Run Code Online (Sandbox Code Playgroud)

从这一点来说,你可以做些什么就取决于你,但这在技术上肯定是可行的.


Sam*_*ler 8

我不建议这样做,因为这bad_alloc意味着你已经失去记忆.最好放弃而不是试图恢复.不过这是你要求的解决方案:

try {
    foo();
} catch ( const std::bad_alloc& e ) {
    return -1;
}
Run Code Online (Sandbox Code Playgroud)

  • @Wolf:问题明确地说"停止这个错误终止我的程序(我想做的就是返回-1)" - 它可能会或者可能没有用,如Konrad的回答中所讨论的那样,但这就是所要求的(不同于你的答案是`exit()`). (2认同)

Tru*_*ueY 5

我可能会建议一个更简单(甚至更快)的解决方案.new如果无法分配内存,operator将返回null.

int fv() {
    T* p = new (std::nothrow) T[1000000];
    if (!p) return -1;
    do_something(p);
    delete p;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我希望这可以帮助你!