New (std::nothrow) vs. New within a try/catch block
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.
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 the std::bad_alloc
propagate back to main
. In a release build, where assert
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 to main
.
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 catching std::bad_alloc
is quite good.
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 the new
) an exception and exit gracefully from the program.
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
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 pass std::nothrow
to new
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.
精彩评论