Automatic memory management myth
and why GC is not enough
NOTE: C+11 standard introduces new smart pointer types to replace std::auto_ptr<>
and boost versions.
At a recent interview, a job candidate ticked me off when we reached one of the topics which is very dear to my heart. We are mostly C++ shop, but many of our job applicants come from .NET background. So during the interview we came to the topic of explicit memory management vs. automatic memory management (a.k.a. garbage collection), and the candidate (with substantial C++ experience) started ranting about how troublesome explicit memory management is, with all this extra caution required to call delete
for every new
, as opposed to simplicity of platforms which support garbage collection.
Well, if you happen to agree with this guy, then you are not doing it right :)
Although GC can be done in C++, that is not the point: the main issue here is that many people are still writing C code which they compile with C++ compiler.
Modern C++ is very different beast from C, and as such provides different patterns for common problems. The concept of smart pointers alleviates much of the manual work that is otherwise needed to handle allocations correctly. The STL comes with std::auto_ptr
, which makes it trivial to ensure the proper deallocation in face of exceptions or normal scope exit.
If you need to handle an array, STL is your friend again: there is std::vector
, and it also takes care of transparent resizing/reallocation, so you don't have to worry about it.
What is (currently) lacking in STL is an implementation of smart pointer with shared semantics. However, there is plenty of such implementations, most notably the excellent boost::shared_ptr
, which is a reference counted implementation that makes sure that the allocated object is deleted when the last reference to it goes away. It will also become a part of new C++ standard, and some vendors support it already.
One issue where GC (at least when implemented with mark-and-sweep algorithms, like in Java or .NET) has an advantage over explicit memory management is the problem of circular references, i.e. when two objects hold a reference to each other. This is really a problem for reference counted implementations, but most cases can be handled by judiciously using weak pointers, and such an implementation is provided by boost::weak_ptr
, which will also be included in new C++ standard.
If you think that this problem is a 'deal breaker' to prefer platforms with GC, bear in mind that the similar problem also exists even there, and that there is a very good reason why Java and .NET both provide WeakReference
class (e.g. see here for one such problem).
What I find particularly annoying is the fact that the only resource which is deemed to be important enough to be automatically handled is memory. For everything else, like database connections, kernel or GDI objects, clients have to explicitly call Dispose()
, and it seems that most developers on managed platforms have no problems with that. A coworker who recently switched from VB to .NET found out hard way that you really have to call Dispose()
on your bitmaps. What happens when you have to share such an object between multiple clients (yes, I know that there are idioms to avoid this resource sharing)? Who calls Dispose()
? Can you be sure that it has not already been disposed?
GC is a nice and useful thing, but it is not a silver bullet, and has its own problems.
Deterministic finalization is what makes it possible in C++ to treat all resources equally: whether it is a memory, a bitmap, a COM object or database connection, with a simple wrapper around it, you can rest assured that the object will be properly released as soon as it is not referenced anymore. Actually, smart pointers are only a special case of what is one of the most powerful concepts in C++, although a bit unfortunately named: RAII
If you are a C++ programmer, please take some time to learn C++ idioms. It will make you a better programmer, and your code a better code.
When you program in C++, make sure it really is C++ and not C.