31

Just recently I switched the language of my project to use C++ from C. With C, I used malloc and after that I check if malloc was successful but with C++, I use 'new' to allocate memory and I would like to know how you would normally check the memory allocation failure.

From my google search, I saw nothrow like the following.

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

I also saw the following.

try{} catch(bad_alloc&) {}

But what about the following? I am using some of chrome library routines to use smart pointers.

For instance, I have the code as follows.

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

It is great to use smart pointers but I am just not sure how I should check if the memory allocation was successful. Do I need to break into two separate statement with nothrow or try/catch? How do you normally do these checks in C++?

Any advice will be appreciated.

gsamaras
  • 71,951
  • 46
  • 188
  • 305
istudy0
  • 1,313
  • 5
  • 14
  • 22
  • What do you plan to do on failure. Not much you can do apart from log. The normal new throws an exception that starts an unwind of the stack to a point where either you exit the application or get to a an event loop. Both place are good for log the failure (thus allowing you to put the logging in one place). – Martin York Jul 26 '11 at 18:29

4 Answers4

26

Well, you call new that throws bad_alloc, so you must catch it:

try
{
    scoped_array<char> buf(new char[MAX_BUF]);
    ...
}
catch(std::bad_alloc&) 
{
    ...
}

or

scoped_array<char> buf(new(nothrow) char[MAX_BUF]);
if(!buf)
{
   //allocation failed
}

What I mean by my answer is that smart pointers propagate exceptions. So if you're allocating memory with ordinary throwing new, you must catch an exception. If you're allocating with a nothrow new, then you must check for nullptr. In any case, smart pointers don't add anything to this logic

gsamaras
  • 71,951
  • 46
  • 188
  • 305
Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
  • 5
    And note that typically you don't wrap every `new` in its own `try` block. Instead, you catch at whatever point in your program you can usefully do something about it. – Steve Jessop Jul 26 '11 at 16:17
20

I hate to say it, but IMO, you're going in the wrong direction (and, unfortunately, the other answers you've gotten haven't really pointed you in the right direction either).

Rather than choosing between different varieties of smart pointer and/or normal vs. nothrow variants of new, you should probably take at least two more steps back from what you're doing, and replace your manually-managed dynamic data structures with collections. This may not always be the right choice, but at least in my experience, it's the right way to go a lot more often than not. The standard library has a number of possibilities (vector, deque, list, set, etc.), and chances are pretty good that you can use one of them rather than dealing directly with new and company at all.

By default, those will use an allocator that ends up using operator new, and throwing in case of failure. You, therefore, normally want to put most code in a try block at a fairly high level, and have a catch clause that deals with having run out of memory there1.

When/if you do need to deal with allocating memory directly, chances are pretty good that you still want to provide an interface similar to that of the standard containers in the library so it'll work with the normal algorithms and iterators. Your initial experience using the existing containers will pay of well when you get to this point, even though it may be a ways down the road.


  1. Or not. It won't necessarily do a lot of good under Linux, for example, which frequently reacts to out of memory situations with something called the OOM-killer. In this case, you won't be able to recover from allocation failure. Either the allocation will succeed, or the process summarily killed.
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • 1
    +1 Best answer: Other answer are a literal attempt to give an answer to a C-programmer. But really they need to re-think how memory management is done. – Martin York Jul 26 '11 at 18:32
  • Thank you very much for your advice. Let me try to implement your advice. – istudy0 Jul 26 '11 at 20:49
  • I do not agree: standard containers also generate allocation, so just to believe the container actually is not enough. In other words, the question still valid, or is even more valid with standard containers. A top try is unmanageable and in the better case provide you a log (but your application is still about to crash). – Adrian Maire Jan 13 '16 at 14:54
  • @AdrianMaire: I said "fairly high level", which is not necessarily the "top" level. The point is that you should let it propagate up to a level where you know how to handle it--essentially all the container itself can do is fail. At a higher level, you may (for example) have other data cached that you can release, to give this allocation a chance of succeeding (or this might just be trying to cache something, and it's just allowed to fail). Or failure may mean the program has to exit. But the container itself can't even guess which applies, so it shouldn't catch the exception. – Jerry Coffin Jan 13 '16 at 16:27
8

In C++ there are 2 primary ways in which new allocates memory and each requires different error checking.

The standard new operator will throw a std::bad_alloc exception on failure and this can be handled like a normal exception

try {
  char* c = new char[100];
} catch (std::bad_alloc&) {
  // Handle error
}

Or alternative the nothrow version of new will simply return NULL on failure

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

I'm curious though as to what you expect to do when the allocation fails? If there is no memory available to allocate your object, there's often very little which can be done in the process.

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • 1
    "there's often very little which can be done in the process." - with the often-ness depending on the value of `MAX_BUF`. – Steve Jessop Jul 26 '11 at 16:18
  • @Steve oh it is possible to correct after a bad alloc. I've just very rarely seen any code which does it correctly. – JaredPar Jul 26 '11 at 16:19
  • 1
    Usually "failing gracefully" is the best that can be hoped for. – Paul R Jul 26 '11 at 16:23
  • @JaredPar: on the plus side, the questioner is a C programmer, and already bothers to check the result of `malloc`. To me, that suggests a better-than-average chance of being capable of dealing with out-of-memory :-) Obviously it depends a lot on the kind of code, as well -- for some kind of service (or web server, etc), the ideal result to bad_alloc might be to return a response to the client indicating failure, which might require *much* less memory than whatever failed. For a desktop app, at bare minimum you need to do your best to save user data. And so on. – Steve Jessop Jul 26 '11 at 16:27
  • @Steve the problem with out of memory handlers though is there very rarely tested and over time silent memory allocations get inserted into the handler. Seen that happen a number of times (and crash reports where there was clearly an OOM followed by an OOM in the handler). IMHO if you're not explicitly running checks that force OOM's and your handlers your better off just crashing the process and not bothering to handle. – JaredPar Jul 26 '11 at 16:29
  • @JaredPar: I've seen two different kinds of OOM, though - those where the system is chronically OOM, and you're in a straightjacket, vs those where one part of the app uses a heck of a lot of memory for a limited period of time, and there's plenty free just not enough for that operation. It's relatively easy to handle the latter kind, and relatively easy to test it too (assuming OOM actually happens properly on your system). IMO code that handles the latter but not the former is still worth having, although not ideal. – Steve Jessop Jul 26 '11 at 16:33
  • Just out of curiosity, can you recommend any example of the second form? – istudy0 Jul 26 '11 at 16:41
1

You'll still need to check for a memory allocation failure.

Either

scoped_array<char> buf;

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

Or

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

if( buf.get() == NULL ) {
   // Handle the failure
}
Praetorian
  • 106,671
  • 19
  • 240
  • 328