MFC prevents bad_alloc from being thrown

According to the C++ standard, the new operator should throw std::bad_alloc if it fails. This will typically happen if your process has run out of memory. However, this isn’t the case if your program uses the (rather outdated) Microsoft Foundation Classes. In this post, we’ll look at what’s going on, and what you can do about it.

The normal approach

In practice, catching bad_alloc isn’t usually very useful. If your program has truly run out of available memory then there’s often very little you can do to salvage the situation. However, catching it can occasionally come in handy if your program needs an unusually large chunk of contiguous memory. In these cases, you’d detect a failed allocation something like this:

try {
    // allocate some stuff
}
catch (const std::bad_alloc &) {
    // oh dear, it failed...
}

You could catch by value if preferred, and it doesn’t have to be const. If your program/compiler/environment is standards-compliant, you just need to be looking out for bad_alloc somehow.

However, if your program links against the MFC libraries, then the bad_alloc handler will never be triggered.

What happens instead?

Without any MFC-aware exception handling, your program will probably display a simple little dialog saying “Out of Memory”, and that’s it. It might seem to continue OK, or it might eventually crash or have other problems, depending on how you’ve written it. It may not say anything at all about an unhandled exception, because MFC seems to absorb it (sometimes).

As far as I can tell, MFC replaces the standard new operator with its own version. When it fails, instead of throwing bad_alloc, it will throw a pointer to a CMemoryException object.

The pointer is important. It means you can’t catch it by value or reference.

In practice, this means you need to detect a failed allocation something like this instead:

try {
    // allocate some stuff
}
catch (const CMemoryException *) {
    // oh dear, it failed...
}

As a side note, standard MFC exception handling looks a little different. It uses its own macros such as CATCH(), which inconveniently disguises the fact that the type being thrown/caught is actually a pointer.

I have a strong dislike for abusing macros in this way as it hides what’s actually going on. There are situations where macros are beneficial, but it’s not very often.

Why does this insanity happen?

MFC is simply very out-dated. Throwing bad_alloc wasn’t standard back when MFC was first introduced, and this is something they’ve simply never fixed. Even though MFC is still maintained (to a certain extent), I suspect they will never fix it because they don’t really seem to want people using it anymore.

Portable solutions

Unfortunately, you can’t catch an incomplete type (or even a pointer to one). This means that everything which is to catch CMemoryException needs to see its full declaration, which creates a frustrating dependence on the MFC headers.

That’s not a problem if it’s within code which is tied to MFC anyway, such as a dialog’s event handler. However, it’s not good for code which you might want or need to remain portable, e.g. a class which you might reuse in another application.

Avoid the new operator

This should go without saying, but modern C++ code shouldn’t really need the new operator very much anyway. Where possible, prefer smart pointers (e.g. std::shared_ptr along with std::make_shared()) and container templates (e.g. std::vector). If you’re working with the MSVC compiler, they all seem to cope OK even when MFC makes a mess of things, meaning you hopefully won’t need to worry about failed allocation exceptions.

Scatter gun approach

If you need to allocate data yourself, one way to avoid the portability problem altogether is simply to do a catch-all, such as this:

try {
    // allocate some stuff
}
catch (...) {
    // oh dear, it failed...
}

That will catch (and absorb) any exception which happens to be thrown. If the only code within the try block is an allocation of Plain Old Data (e.g. char or int) then that’s probably safe. CMemoryException and bad_alloc are probably the only exceptions you’ll ever see.

However, in pretty much all other circumstances, there is a risk of hiding some other exception which may be important. Because of this, you need to use care with this approach.

typedef

A reasonably painless approach which I’ve used successfully is to typedef the exception type. In a header file somewhere (probably stdafx.h if you’re using it), create a typedef like this:

typedef const CMemoryException * FailedAllocException;

This allows you to create exception handlers like this:

try {
    // allocate some stuff
}
catch (FailedAllocException) {
    // oh dear, it failed...
}

If at any point you want to use the code in a non-MFC environment, simply change the typedef to something like this:

typedef const std::bad_alloc & FailedAllocException;

nothrow

In theory, nothrow new should be a sensible alternative, e.g.:

char * buffer = new (std::nothrow) char [1024];

This is supposed to avoid throwing exceptions entirely. If something goes wrong, it will instead return a null pointer. Unfortunately, MFC seems to screw this up as well (I couldn’t get it work on VC12 anyway).

Replace the new operator or handler

If you really want to live dangerously, you could entirely replace the new operator, or replace the handler function which is called when new fails (by calling set_new_handler). This is generally not a good idea though as it could break other code which may be relying on specific/standard behaviour.

2 comments on “MFC prevents bad_alloc from being thrown

Leave a Reply

Your email address will not be published. Required fields are marked *