有一种名为foo
的方法有时会返回以下错误:
terminate called after throwing an instance of 'std::bad_alloc'
what(): std::bad_alloc
Abort
有没有办法可以使用try
- catch
块来阻止此错误终止我的程序(我想要做的就是返回-1
)?
如果是这样,它的语法是什么?
我还能如何处理C ++中的bad_alloc
?
答案 0 :(得分:77)
通常,您不能且不应尝试来回复此错误。 bad_alloc
表示无法分配资源,因为没有足够的可用内存。在大多数情况下,您的程序无法应对这种情况,并且很快就会终止是唯一有意义的行为。
更糟糕的是,现代操作系统经常过度分配:malloc
和new
将始终返回有效指针,即使技术上没有(或没有足够)可用内存,所以{{1永远不会被抛出,或者至少不是内存耗尽的可靠标志。相反,尝试访问分配的内存将导致错误,这是不可捕获的。
捕获std::bad_alloc
时唯一可以做的就是记录错误,并尝试通过释放未完成的资源来确保程序安全终止(但这是在正常的堆栈展开过程中自动完成的。如果程序适当地使用RAII,则会抛出错误。
在某些情况下,程序可能会尝试释放一些内存并重试,或使用辅助内存(=磁盘)而不是RAM,但这些机会仅存在于非常特定的情况下。
答案 1 :(得分:36)
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;
}
在上面的示例中,operator new
(很可能)将无法为100,000,000个整数分配空间,并且将调用函数outOfMemHandler()
,并且程序将在issuing an error message.之后中止
如此处所示,new
运算符在无法满足内存请求时的默认行为是重复调用new-handler
函数,直到找到足够的内存或没有更多的新处理程序。在上面的示例中,除非我们调用std::abort()
,否则outOfMemHandler()
将为called repeatedly。因此,处理程序应该确保下一个分配成功,或者注册另一个处理程序,或者不注册处理程序,或者不返回(即终止程序)。如果没有新的处理程序并且分配失败,则操作员将抛出异常。
new_handler
和set_new_handler
? new_handler
是指向函数的指针的typedef,该函数接受并不返回任何内容,而set_new_handler
是一个获取并返回new_handler
的函数。
类似的东西:
typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();
set_new_handler的参数是一个指向函数运算符new
的指针,如果它不能分配所请求的内存则应该调用它。它的返回值是指向先前注册的处理函数的指针,如果没有先前的处理程序,则返回null。
鉴于new
的行为,精心设计的用户程序应通过提供适当的new_handler
来处理内存不足情况,该set_new_handler
会执行以下操作之一:
提供更多可用内存:这可能允许运算符new循环内的下一次内存分配尝试成功。实现此目的的一种方法是在程序启动时分配一大块内存,然后在第一次调用new-handler时释放它以便在程序中使用。
安装一个不同的新处理程序:如果当前的new-handler不能再提供更多可用内存,并且还有另一个新处理程序可以,那么当前的new-handler可以在其位置安装另一个新处理程序(通过调用set_new_handler
)。下一次operator new调用new-handler函数时,它将获得最近安装的函数。
(这个主题的变体是一个新的处理程序来修改它自己的行为,所以下次调用它时,它会做一些不同的事情。实现这一点的一种方法是让new-handler修改static,namespace-影响新处理程序行为的特定或全局数据。)
卸载新处理程序:这是通过将空指针传递给operator new
来完成的。如果没有安装新的处理程序,std::bad_alloc
将在内存分配失败时抛出异常((可转换为)std::bad_alloc
)。
抛出异常可转换为operator new
。 abort
不会捕获此类异常,但会传播到发起内存请求的站点。
不返回:致电exit
或{{1}}。
答案 2 :(得分:30)
你可以像任何其他例外一样捕捉它:
try {
foo();
}
catch (const std::bad_alloc&) {
return -1;
}
从这一点开始你可以做些什么就取决于你,但这在技术上肯定是可行的。
答案 3 :(得分:8)
我不建议这样做,因为bad_alloc
表示你内存不足。最好放弃而不是试图恢复。不过这是你要求的解决方案:
try {
foo();
} catch ( const std::bad_alloc& e ) {
return -1;
}
答案 4 :(得分:5)
我可能会建议一个更简单(甚至更快)的解决方案。如果无法分配内存,new
运算符将返回null。
int fv() {
T* p = new (std::nothrow) T[1000000];
if (!p) return -1;
do_something(p);
delete p;
return 0;
}
我希望这可以提供帮助!
答案 5 :(得分:1)
以受控方式让您的 foo计划exit :
#include <stdlib.h> /* exit, EXIT_FAILURE */
try {
foo();
} catch (const std::bad_alloc&) {
exit(EXIT_FAILURE);
}
然后编写一个调用实际程序的 shell程序。由于地址空间是分开的,因此shell程序的状态总是很明确。
答案 6 :(得分:0)
我想贡献我刚刚遇到的bad_alloc
的一个原因:
我有一个foo
成员的课程T bar
。在构造函数中,我想使用参数中的值初始化成员:
foo::foo(T baz)
: bar(bar)
{
}
问题是我输入了错误的参数(“ baz”而不是“ bar”)。 这导致构造函数使用自身(未初始化的值!)而不是参数来初始化bar。
我不得不使用valgrind来发现这一点。