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 Class library. 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 be useful if your program needs an unusually large chunk of contiguous memory. In these cases, you might detect a failed allocation like this:
1
2
3
4
5
6
7
8
9
10
char *buffer = nullptr;
try
{
// Attempt to allocate a large block of memory
buffer = new char[10000000];
}
catch (const std::bad_alloc &)
{
// Allocation failed. Attempt to recover here...
}
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.
This means you need to detect a failed allocation something like this instead:
1
2
3
4
5
6
7
8
9
10
char *buffer = nullptr;
try
{
// Attempt to allocate a large block of memory
buffer = new char[10000000];
}
catch (const CMemoryException *)
{
// Allocation failed. Attempt to recover here...
}
Standard MFC exception handling actually looks a little different. It uses its own macros such as CATCH()
. These disguise the fact that the type being thrown/caught is actually a pointer. Using macros this way is generally bad practice by modern standards as it obscures the underlying code. It’s not a technique which should be copied.
(In fact, modern C++ should avoid the use of macros altogether where possible. There are some situations where they are the right tool for the job, but they should be used very sparingly.)
Why does this happen?
MFC is simply very out-dated. Throwing bad_alloc
wasn’t standard back when MFC was first introduced, and changing its behaviour now could cause problems for existing code. 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 in C++. You can’t even catch a pointer to one, so a forward declaration doesn’t work. Everything which needs to catch CMemoryException
needs to see its full declaration, creating 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 needs to remain portable, such as a class which you might reuse in another application.
Avoid the new operator
Since the problem is that the new
operator is being replaced, you could simply try to avoid using it. This isn’t very practical for any significant program, but it might be feasible in limited situations.
It’s also possible that you could use smart pointers and standard library containers instead, such as std::shared_ptr
and std::vector
. These don’t always use the new
operator internally for a variety of reasons. However, it does depend on a few factors so it may be risky to rely on.
Catch everything
A more reliable alternative would be to use a catch-all around potentially risky allocations:
1
2
3
4
5
6
7
8
9
10
char *buffer = nullptr;
try
{
// Attempt to allocate a large block of memory
buffer = new char[10000000];
}
catch (...)
{
// Allocation failed. Attempt to recover here...
}
That will catch any exception which is thrown. If the only code within the try
block is an allocation of Plain Old Data (e.g. char
or int
) then this is probably safe. CMemoryException
and bad_alloc
should be the only exceptions you’ll see.
However, in pretty much all other circumstances, there is a risk of hiding some other exception which may be important. As such, you need to be careful 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:
1
typedef const CMemoryException * FailedAllocException;
This allows you to create exception handlers like this:
1
2
3
4
5
6
7
8
9
10
char *buffer = nullptr;
try
{
// Attempt to allocate a large block of memory
buffer = new char[10000000];
}
catch (FailedAllocException)
{
// Allocation failed. Attempt to recover here...
}
If you want to use the code in a non-MFC environment, simply change the typedef
to something like this:
1
typedef const std::bad_alloc & FailedAllocException;
It’s important to note that the CMemoryException
instance should technically be freed otherwise you will have a memory leak. This could perhaps be done with a conditional macro.
nothrow
In theory, the nothrow
overload of the new
operator should be a viable alternative:
1
char *buffer = new (std::nothrow) char[10000000];
This is supposed to avoid throwing exceptions entirely. If something goes wrong, it will instead return a null pointer. Unfortunately, MFC seems to break this as well. I couldn’t get it to work on VC12 anyway.
Replace the new operator or handler
A more radical solution could involve replacing the new
operator, or replacing the handler function which is called when new
fails (calling set_new_handler()
). This generally isn’t a good idea though as it could break other code which may be relying on specific/standard behaviour.
Conclusion
As already mentioned, the MFC library is very outdated, having first been released in 1992. That was several years before the core of the C++ language was standardised. As a result, its design doesn’t fit well with modern code, despite having been an impressive feat of software engineering in its day.
The difficulty with the new
operator is just one of many issues arising from its age. MFC is technically still under development by Microsoft, although I doubt it will ever see a major overhaul.
Consequently, I strongly advise against using it in any new project. Where possible, older projects should be migrated to another GUI library, or possibly to another language such as C# if appropriate.