如何使用new运算符检查内存分配失败?

31

最近我把项目的语言从C转换为C++。在使用C时,我使用malloc函数分配内存并检查是否分配成功,但在使用C++时,我使用“new”关键字来分配内存,我想知道您通常如何检查内存分配失败。

在我的谷歌搜索中,我看到了以下的nothrow。

char *buf = new (nothrow)char[10];

我也看到了以下内容。

try{} catch(bad_alloc&) {}

但以下代码怎么办?我正在使用chrome库例程来使用智能指针。

例如,我的代码如下所示。

scoped_array<char> buf(new char[MAX_BUF]);

使用智能指针非常好,但我不确定应该如何检查内存分配是否成功。 我需要使用nothrow或try/catch将其拆分为两个单独的语句吗? 在C++中,通常如何进行这些检查?

非常感谢您的任何建议。


你打算在失败时做什么?除了记录日志之外,你没有太多可做的。正常情况下,新的异常会引发堆栈展开到一个点,其中你要么退出应用程序,要么进入事件循环。在这两个地方都可以记录失败(因此允许您将日志记录放在一个地方)。 - Martin York
4个回答

27

你调用了会抛出 bad_allocnew,所以你必须捕获它:

try
{
    scoped_array<char> buf(new char[MAX_BUF]);
    ...
}
catch(std::bad_alloc&) 
{
    ...
}
或者
scoped_array<char> buf(new(nothrow) char[MAX_BUF]);
if(!buf)
{
   //allocation failed
}
我回答的意思是智能指针可以传递异常。因此,如果您使用普通的throwing new分配内存,则必须捕获异常。如果您使用nothrow new进行分配,则必须检查nullptr。在任何情况下,智能指针对此逻辑没有任何添加。

5
请注意,通常情况下你不需要为每个 new 语句单独编写一个 try 块。相反,你可以在程序的任何有用的点上捕获异常并进行处理。 - Steve Jessop
在你复制粘贴之前,请先阅读另一个答案 - undefined

21

我不想说,但在我看来,你正在走向错误的方向(而且,不幸的是,你得到的其他答案也没有真正指出正确的方向)。

与其在智能指针的不同变体和/或常规 vs. nothrow 变体之间进行选择,你应该可能退后至少两步,用集合替换手动管理的动态数据结构。这可能并不总是正确的选择,但至少在我的经验中,它比不这样做要更正确。标准库有许多可能性(vector、deque、list、set等),很有可能你可以使用其中之一,而不必直接处理new和相关内容。

默认情况下,它们将使用分配器,最终使用operator new,并在失败时抛出异常。因此,通常希望将大部分代码放在相当高的级别的try块中,并具有一个catch子句,在那里处理内存耗尽的情况1

如果/当你需要直接处理内存分配时,很有可能你仍然希望提供类似于库中标准容器的接口,以便它可以与正常的算法和迭代器一起使用。即使这可能还有一段路要走,但你使用现有容器的初始经验将会在这个阶段得到很好的回报。


  1. 不一定。例如在 Linux 下,这并不一定会有太好的效果,因为 Linux 经常会对内存不足时采取一些叫做 OOM-killer 的措施。在这种情况下,您将无法从分配失败中恢复。要么分配成功,要么进程被迅速终止。

1
+1 最佳答案:其他答案只是试图给C程序员一个字面上的答案。但实际上,他们需要重新思考内存管理的方式。 - Martin York
非常感谢您的建议。让我尝试实施您的建议。 - istudy0
我不同意:标准容器也会生成分配,所以仅仅相信容器是不够的。换句话说,问题仍然存在,甚至在使用标准容器时更为突出。顶层尝试是无法管理的,在最好的情况下只能提供日志(但您的应用程序仍将崩溃)。 - Adrian Maire
1
@AdrianMaire:我说的是“相当高的水平”,这不一定是“顶级”水平。关键是要让它传播到你知道如何处理它的水平--基本上,容器本身只能失败。在更高的层面上,你可能(例如)有其他数据缓存可以释放,以使此分配有机会成功(或者这只是尝试缓存某些内容,并允许其失败)。或者失败可能意味着程序必须退出。但是容器本身甚至无法猜测哪种情况适用,因此不应捕获异常。 - Jerry Coffin
@JerryCoffin 你也可以指出,尽可能使用RAII来实例化std容器,而不是使用new/delete,以避免内存泄漏等问题。即使在复杂的C++程序中,我很少甚至从不手动使用new - undefined

8
在C++中,new有两种主要的分配内存的方式,并且每种方式都需要不同的错误检查。 标准的new运算符在失败时会抛出一个std::bad_alloc异常,可以像正常异常一样进行处理。
try {
  char* c = new char[100];
} catch (std::bad_alloc&) {
  // Handle error
}

或者使用nothrow版本的new,在失败时将仅返回NULL

char* c = new (std::nothrow) char[100];
if (!c) {
  // Handle error
}

不过,我很好奇当分配内存失败时,您希望做什么呢?如果没有可用内存来分配您的对象,则通常很少能在进程中进行操作。


1
根据MAX_BUF的值,通常在过程中很少能做些什么。 - Steve Jessop
@Steve 哦,即使出现了 bad alloc 也有可能进行纠正。但我很少见到能够正确处理的代码。 - JaredPar
1
通常情况下,“优雅地失败”是最好的期望。 - Paul R
@JaredPar:好的一面是,提问者是C程序员,并且已经想过要检查malloc的结果。对我来说,这表明他处理内存不足的能力比平均水平要好 :-) 当然,这在很大程度上取决于代码类型——对于某些服务(或Web服务器等),坏掉的理想结果可能是返回给客户端表示失败的响应,这可能需要比失败时更少的内存。对于桌面应用程序,至少你需要尽力保存用户数据。诸如此类。 - Steve Jessop
@JaredPar:我见过两种不同类型的OOM,一种是系统经常OOM,你束手无策;另一种是应用程序的某个部分在有限的时间内使用了大量内存,虽然还有很多空闲内存,但不足以支持该操作。处理后者相对容易,并且测试也相对容易(假设OOM在您的系统上确实发生)。在我看来,处理后者而不是前者的代码仍然值得拥有,尽管不是理想的情况。 - Steve Jessop
显示剩余2条评论

1

你仍需要检查内存分配失败。

要么

scoped_array<char> buf;

try {
  buf.reset( new char[MAX_BUF] );
} catch( std::bad_alloc& ) {
  // Handle the failure
}

或者

scoped_array<char> buf( new(std::nothrow)char[MAX_BUF] );

if( buf.get() == NULL ) {
   // Handle the failure
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接