43

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.

Anne Quinn
  • 12,609
  • 8
  • 54
  • 101
  • 2
    Use exceptions. They make your code more meaningful, local, maintainable and scalable. – Kerrek SB Sep 01 '11 at 23:12
  • 3
    Regarding readability only, definitely the one without exception handling. Regarding performance, maybe also the `nothrow`. But I'm sure over-exceptionists will argue. But if you just want a simple assertion failure, you can also use the throwing variant and omit the exception handler ;) – Christian Rau Sep 01 '11 at 23:13
  • 4
    What's the point of that code? You're catching a specific, meaningful exception and then creating a meaningless one through `assert()`... – Blindy Sep 01 '11 at 23:15
  • @Kerrek - I added maintainability to the list of things, can't believe I left that out. I actually have no insight into how exceptions work to accomplish those things you said, but I hope the answers will help shed light on that for me :] – Anne Quinn Sep 01 '11 at 23:17
  • @Blindy - It's just there for example. In my code, I have my own MyAssert(Problem p, char * arg) function that takes source/line macros so I know where it occurred. As often as memory is allocated, that's as meaningful as time allows. – Anne Quinn Sep 01 '11 at 23:22
  • 1
    I dunno, bad example. Use malloc() for a C-array. Trying to deal with std::bad_alloc() is almost always a mistake. It is effectively an asynchronous exception. When you start to consume more than half of the address space, it is high time to consider switching to a 64-bit operating system. – Hans Passant Sep 01 '11 at 23:29
  • 2
    This is just rehashing returns codes vs exceptions for error reporting. – Ben Voigt Sep 01 '11 at 23:32
  • @Ben - I have no qualms against exceptions.. I'm just wondering how it fairs in this situation with `new` is all. (I didn't doubt this question could be taken as a shot against Exceptions, but you'll have to trust me that is not the intention) – Anne Quinn Sep 01 '11 at 23:51
  • @Christian: the problem with the example is that it's too small. In practice, you'd catch exceptions at a point where you are in a position to react meaningfully, so it's unlikely that that'd be right there at the allocation site, because from there all you can do is propagate the error (as the OP attempts). – Kerrek SB Sep 01 '11 at 23:52
  • @Christian: exceptions are usually faster than `if` for exceptional condition (they are faster when not thrown but much slower when thrown). I would not, however, use this particular fact as a basis for decision, this is quite implementation dependent and mostly a micro-optimization. – Matthieu M. Sep 02 '11 at 07:06
  • @Matthieu I wasn't completely sure myself and I too would not only make it dependent on a negligable performance difference, but the OP explicitlc asked for it. – Christian Rau Sep 02 '11 at 12:24
  • The nothrow version *can be* (but not necessarily *is*) faster, particularly if you're compiling with exceptions disabled. If you're compiling with exceptions enabled, I doubt there's a significant performance between the two here, as allocating memory is usually a pretty "expensive" operation. – Cornstalks Sep 03 '14 at 02:00

4 Answers4

52

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 asserts. 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.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 3
    This is a pretty convincing argument, I was not aware exceptions could climb the call stack to find a catch block. ... I am not aware of a lot of things exceptions can do. Might be time I finally learn it after all these years, aha – Anne Quinn Sep 01 '11 at 23:38
14

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.

Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • I didn't consider that, I suppose mixing and matching for the situation wouldn't be bad style in that case. Obviously the two ways exist for that reason. Thanks! – Anne Quinn Sep 01 '11 at 23:46
  • 4
    That's a nice answer. It sums up the entire philosophy of exceptions: Use exceptions for exceptional situations, and catch them where they can be meaningfully handled. On the other hand, for a non-exceptional runtime situation that you can and want to deal with locally, don't use exceptions. – Kerrek SB Sep 01 '11 at 23:55
7

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.

Chad
  • 18,706
  • 4
  • 46
  • 63
  • So the performance penalty of exceptions would be less than equality checking of pointers, assuming nothing goes wrong? If that's the case, I see no reason to not use exceptions now. (And yeah, I try to wrap all my allocation needs to a small objects allocator, keeps all the memory stuff in one tidy spot too) – Anne Quinn Sep 01 '11 at 23:43
  • There is still some penalty when using exceptions (the the language does for you), such as setting up the stack appropriately, SEH processing, etc. Generally I wouldn't expect there to be a measurable difference between the two in a case where "nothing goes wrong.". In the case of failure the exception code will be much more involved, but it cleans up the code by not requiring the `NULL` checks. It's really a matter of personal preference by that point though. – Chad Sep 01 '11 at 23:46
  • 2
    In the GCC implementation, exceptions don't cost you anything (apart from perhaps a slightly worse memory locality) if they're not used -- you only pay when the exception is actually thrown. – Kerrek SB Sep 01 '11 at 23:54
  • @Kerrek SB , Chad - That's okay then. I assume if there's any performance problems, it's going to be my fault more than it would be exception handling or anything else anyway. Just so long as it's not a real noticeable difference, then the only factor is readability and maintainability for me. – Anne Quinn Sep 01 '11 at 23:58
-4

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.

Jamal
  • 763
  • 7
  • 22
  • 32
  • If your main object have subobjects, then those can also throw bad_alloc if they allocate memory... so you won't know for sure if it is the main allocation that has failed. However, you don't care as the object is not usable anyway and the compiler will take care of doing required cleanup of partially constructed objects. **I would suggest you to learn the language first** before gaving arbitrary answers that are misleading. In particular, your recommandation to look for exceptions when creating objects is bad if it mean to put try/catch around every allocation. – Phil1970 Sep 03 '17 at 11:58
  • I would recommand you to read **Exceptional C++** and other similar books before writing any code used in professional applications. – Phil1970 Sep 03 '17 at 11:59