Sponsored Link •
Bill Venners: Another technique I never heard about when I was programming C++ is "resource allocation is initialization." Could you explain that technique with respect to memory management, resource management, and exception safety?
Bjarne Stroustrup: If I create 10,000 objects and have pointers to
them, I need to delete those 10,000 objects, not 9,999, and not 10,001. I don't know
how to do that. If I have to handle the 10,000 objects directly, I'm going to
screw up. This is what I meant when I said earlier [in Part I] that if you use
new the way you used
malloc, you're going to get in
trouble. So, quite a long time ago I thought, "Well, but I can handle a
low number of objects correctly." If I have a hundred objects to deal with, I can be
pretty sure I have correctly handled 100 and not 99. If I can get then number down to 10 objects, I start
getting happy. I know how to make sure that I have correctly handled 10 and not just 9.
For example, a container is a systematic way of dealing with objects. Some of the things you can have in a container are pointers to other objects, but the container's constructors and destructor can take care of contained objects. The name of the game here is to get allocation out of the way so you don't see it. If you don't directly allocate something, you don't directly deallocate it, because whoever owns it deals with it. The notion of ownership is central. A container may own its objects because they are stored directly. A container of pointers either owns the pointed-to objects or it doesn't. If it contains 1000 pointers, there's only one decision to make. Does it own them, or doesn't it? That's a 1000 to 1 reduction in complexity. As you apply this technique recursively and in as many places as you can, allocation and deallocation disappear from the surface level of your code.
The next thing you notice is that this is a general notion of a resource. How do you manage a file? In the old way you have a file pointer. You initialize the file pointer by doing open, and you have to remember to do a close. Well, as I just said you shouldn't have naked pointers with allocations sitting around, and a open operation really is an allocation of a file handle. So instead, we create a resource object for a file, a file handle. Initialization of a file handle opens the file. If the constructor successfully opens the file, the destructor will eventually close it again. So "resource acquisition is initialization" is a consequence of the view of hiding allocation and deallocation by dealing with it in constructors and destructors of classes. The "resource acquisition is initialization" technique, sometimes called RAII, is a clumsy name for a central concept. The "resource acquisition is initialization" technique also happens to be necessary for exception handling, because a major part of exception handling is keeping your program in a reasonable state. That means not leaking resources or having broken invariants, so it fundamentally involves the same resource management problem. Again, the main tool for resource management is constructors and destructors.
Bill Venners: So exception safety means that if an exception flies up through my class that I clean up, close any open resources that need to be closed on the way out, and ensure my invariants are still true on the way out?
Bjarne Stroustrup: That's right. That's basically it. There's almost a whole theory about this, but people can read about that in Appendix E of The C++ Programming Language, Third Edition. If they have a third edition without an Appendix E, they should upgrade to a modern version. And if they're really stone broke and can't afford to upgrade, they can download Appendix E from my homepages. But basically, if your C++ book doesn't have a section on exception safety, it's time to upgrade.
Exceptions signal that something bad (or at least unanticipated) has happened, and you want somebody to help you out of the mess. For that to work, before you get out by throwing an exception, you have to make sure you've cleaned up your local mess. That is, you don't just allocate an object on the heap and then throw an exception, because that would "leak" that object. If you allocated it, you must either delete it or transfer its ownership to someone else before throwing the exception. The exception then goes up the call chain, and at each level the function has to make sure that it releases any resources it has acquired. If you don't use "resource acquisition is initialization", you have to write a try block, you have to provide a catch-everything clause, you have to do whatever cleanup has to be done and then rethrow. It's like writing finally blocks in Java. If you forget to write a finally block you have a bug. You have to get it right every single time throughout the code, and I don't think that's likely. It's a bug source. The simple and manageable way to ensure exception safety to use "resource acquisition is initialization".
Come back Monday, December 1 for the next installment of a conversation with Ward Cunningham. If you'd like to receive a brief weekly email announcing new articles at Artima.com, please subscribe to the Artima Newsletter.
Bjarne Stroustrup is author of The C++ Programming Language, which
is available on Amazon.com at:
Bjarne Stroustrup is author of The Design and Evolution of C++, which
is available on Amazon.com at:
Bjarne Stroustrup's home page:
Bjarne Stroustrup's page about the C++ Programming Language:
Publications by Bjarne Stroustrup:
Interviews with Bjarne Stroustrup:
Bjarne Stroustrup's FAQ:
Bjarne Stroustrup's C++ Style and Technique FAQ:
Bjarne Stroustrup's C++ Glossary:
Libsigc++ Callback Framework for C++:
C++ Boost, peer-reviewed portable C++ source libraries:
Al Stevens' review of The C++ Programming Language, by Bjarne Stroustrup: