New (std::nothrow) 与 try/catch 块中的 New
我在学习new
后做了一些研究,与我习惯的malloc()
不同,分配失败时不会返回NULL,并且发现有两种不同的方法来检查是否new 是否成功。这两种方法是:
try
{
ptr = new int[1024];
}
catch(std::bad_alloc& exc)
{
assert();
};
我
ptr = new (std::nothrow) int[1024];
if(ptr == NULL)
assert();
相信这两种方法可以实现相同的目标(当然,如果我错了,请纠正我!),所以我的问题是:
哪种方法是检查 new
是否更好的选择成功了,完全基于可读性、可维护性和性能,同时忽略了事实上的 C++ 编程约定。
I did some research after learning new
, unlike malloc()
which I am used to, does not return NULL for failed allocations, and found there are two distinct ways of checking whether new had succeeded or not. Those two ways are:
try
{
ptr = new int[1024];
}
catch(std::bad_alloc& exc)
{
assert();
};
and
ptr = new (std::nothrow) int[1024];
if(ptr == NULL)
assert();
I believe the two ways accomplish the same goal, (correct me if I am wrong of course!), so my question is this:
which is the better option for checking if new
succeeded, based entirely on readability, maintainability, and performance, while disregarding de-facto c++ programming convention.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
考虑一下你在做什么。你正在分配内存。如果由于某种原因内存分配无法工作,您可以
断言
。如果您只是让std::bad_alloc
传播回main
,这或多或少正是会发生的情况。在发布版本中,assert
是无操作,当您的程序尝试访问内存时将会崩溃。因此,这与让异常冒泡是一样的:停止应用程序。所以问自己一个问题:您真的需要关心内存不足时会发生什么吗?如果您所做的只是断言,那么异常方法会更好,因为它不会用随机的
assert
来扰乱您的代码。您只需让异常回退到main
即可。如果您确实有一个特殊的代码路径,以防您无法分配内存(也就是说,您实际上可以继续运行),则异常可能会也可能不会,具体取决于代码路径是什么。如果代码路径只是通过将指针设置为 null 来设置的开关,则
nothrot
版本会更简单。相反,如果您需要做一些完全不同的事情(从静态缓冲区中提取数据,或者删除一些内容,或者其他什么),那么捕获 std::bad_alloc 就非常好。Consider what you are doing. You're allocating memory. And if for some reason memory allocation cannot work, you
assert
. Which is more or less exactly what will happen if you just let thestd::bad_alloc
propagate back tomain
. In a release build, whereassert
is a no-op, your program will crash when it tries to access the memory. So it's the same as letting the exception bubble up: halting the app.So ask yourself a question: Do you really need to care what happens if you run out of memory? If all you're doing is asserting, then the exception method is better, because it doesn't clutter your code with random
assert
s. You just let the exception fall back tomain
.If you do in fact have a special codepath in the event that you cannot allocate memory (that is, you can actually continue to function), exceptions may or may not be a way to go, depending on what the codepath is. If the codepath is just a switch set by having a pointer be null, then the
nothrow
version will be simpler. If instead, you need to do something rather different (pull from a static buffer, or delete some stuff, or whatever), then catchingstd::bad_alloc
is quite good.这取决于分配发生地点的上下文。如果即使分配失败您的程序也可以继续(可能向调用者返回错误代码),则使用 std::nothrow 方法并检查 NULL。否则,您将使用异常来控制流,这不是一个好的做法。
另一方面,如果您的程序绝对需要成功分配该内存才能运行,请使用 try-catch 来捕获(不一定位于 new 的附近) ) 异常并从程序中正常退出。
It depends on the context of where the allocation is taking place. If your program can continue even if the allocation fails (maybe return an error code to the caller) then use the
std::nothrow
method and check for NULL. Otherwise you'd be using exceptions for control flow, which is not good practice.On the other hand, if your program absolutely needs to have that memory allocated successfully in order to be able to function, use
try-catch
to catch (not necessarily in the immediate vicinity of thenew
) an exception and exit gracefully from the program.从纯粹的性能角度来看,这并不重要。异常处理存在固有的开销,尽管这种开销通常值得在应用程序可读性和维护方面进行权衡。这种性质的内存分配失败不应出现在应用程序的 99% 情况下,因此这种情况应该很少发生。
从性能角度来看,您通常希望避免使用标准分配器,因为它的性能相对较差。
综上所述,我通常接受异常抛出版本,因为通常我们的应用程序处于这样一种状态:如果内存分配失败,除了使用适当的错误消息优雅退出之外,我们几乎无能为力,并且我们通过不需要
NULL
检查我们新分配的资源,因为根据定义,分配失败会将范围移出重要的位置。From a pure performance perspective it matters little. There is inherent overhead with exception handling, though this overhead is generally worth the trade off in application readability and maintenance. Memory allocation failures of this nature should not be in the 99% case of your application, so this should happen infrequently.
From a performance perspective you generally want to avoid the standard allocator due to its relatively poor performance anyway.
All this said, I generally accept the exception throwing version because generally our applications are in a state where if memory allocation fails, there is little we can do other than exit gracefully with an appropriate error message, and we save performance by not requiring
NULL
checking on our newly allocated resources because by definition an allocation failure will move the scope out from where that matters.new
用于创建对象,而不是分配内存,因此您的示例有些人为。对象构造函数如果失败通常会抛出异常。在 Visual Studio 中多次单步执行
new
实现后,我不认为代码会捕获任何异常。因此,在创建对象时查找异常通常是有意义的。我认为仅当内存分配部分失败时才会抛出
std::bad_alloc
。我不确定如果将std::nothrow
传递给new
但对象构造函数抛出异常,会发生什么 - 我读过的文档中存在歧义。两种方法之间的性能差异可能无关紧要,因为大部分处理器时间很容易花费在对象构造函数或搜索堆上。
经验法则并不总是合适的。例如,实时系统通常会限制动态内存分配,因此 new(如果存在)可能会过载。在这种情况下,它可能会使用返回的空指针并在本地处理失败。
new
is used to create objects, not allocate memory, therefore your example is somewhat artificial.Object constructors typically throw if they fail. Having stepped through the
new
implementation in Visual Studio more than a few times, I don't believe that the code catches any exceptions. It therefore generally makes sense to look for exceptions when creating objects.I think
std::bad_alloc
is thrown only if the memory allocation part fails. I'm not sure what happens if you passstd::nothrow
tonew
but the object constructor throws - there is ambiguity in the documents I have read.The difference in performance between the 2 approaches is probably irrelevant since most of the processor time may easily be spent in the object constructor or searching the heap.
A rule-of-thumb is not always appropriate. For example, real-time systems typically restrict dynamic memory allocations, so
new
, if present, would probably be overloaded. In that case it might make use of a returned null pointer and handle failure locally.