I’m often surprised, and occasionally horrified, that Resource Acquisition Is Initialisation isn’t better known than it is amongst programmers who use languages to which it is applicable. It is a simple, eloquent and efficient way to deal with many situations where there is a risk of “leaking” memory or handles, failing to release file locks, etc.
Actually it generally is known by most programmers in applicable languages, and to some extent is supported by many common libraries and tools. Like many idioms and patterns you aren’t learning anything totally new when you learn about it, but you are making it something that you have more consciously integrated into your mental toolkit for solving programming problems.
Or rather, which languages is RAII appropriate for. As will soon be explained RAII depends on the way that constructors and, in particular, destructors, get called and the predictability of this happening.
RAII can be used with:
RAII cannot generally be used with languages that clean up objects using an unpredictable garbage collection, such as Java (however see the Postscript on .NET). If a language guarantees cleanup of all objects before an application shuts down then it may be applicable to some problems.
It’s worth noting that if you are using C++ with garbage collection (which isn’t defined by the language, but which may be provided by a run time) then that garbage collection will generally not apply to objects allocated on the stack and hence RAII can still be used.
Of course these are arguably differences in implementation rather than in the
language itself, it depends on where you draw the line between language and
runtime support. Inability to support RAII is reflected in the actual language of
Java though in its use of the
finally keyword to
indicate actions which must take place after a
block is left whether an exception is thrown or not. Together with the ability to
catch(...) to catch all errors, RAII
finally unnecessary in C++ (which shares
catch with Java).
Generally there will be some points in the execution of any moderately sized program where something is done which necessitates a later “undoing”. Examples of these are:
IMalloc::Alloc, etc. Which must be matched with a call to
Generally the pattern is:
At this level of complexity the only real danger is that we simply forget to release the resource, which, while it does happen, is something that should be quickly caught in any code-review.
The problem becomes more difficult if the period between obtaining and releasing the resource becomes more complicated, with many different points at which the resource should be released:
Here what we had previously labelled “Use Resource” is now more complicated and as such there are frequent points at which we may wish to release the resource.
One solution to this is to make all of this decision-making and forking is to tie all of the different paths together into one self-contained unit and effectively make the above diagram follow the pattern of the first again. This is one of the reasons that many programmers in some languages consider leaving a function or subroutine at more than one point to be poor style (another is that in some languages, it just looks ugly, certainly there seems to be more effort made to have a single point where a function is exited in VB than in C). However if some of the forking might be because of exceptions being thrown then you cannot always ensure that this is done without working against the exception mechanism.
The solution is to make the first and second part of the pattern, obtaining and releasing the resource, self-contained rather than the middle part.
This is easily done with the constructor and destructor of a class.
In C++ the general form is:
//internal representation holding pointers, handles etc.
In VB6 the general form is:
'Internal representation holding handles etc.
Private Sub Class_Initialize()
Private Sub Class_Terminate()
The obvious next step is to add public functions to these classes that enable them to be used directly instead of the lower-level access to the resource. Indeed even if you are unfamiliar with the RAII idiom you have probably built classes just like the above for exactly that reason.
A few things are worth noting here.
The first is that if obtaining the resource requires parameters of some sort (e.g. the name of a file to obtain file handle) then the C++ version can easily accommodate that by taking an argument to the constructor. With VB6 we need to add an initialisation function to actually obtain such a resource. This brings on quite a few issues though at least these issues are isolated to a single class and can largely be dealt with them there. (Sometimes C++ classes are built in similar ways, See [RECTOR] for examples of classes that do this and a justification for the technique, See [STROUSTRUP] for a, to my mind more convincing, argument against this).
The second is that the constructor and destructor of the C++ version are inline. This is typical of such classes, where the actual code we need to use to execute is pretty simple often a single call into a library routine. These classes often have nothing but inline functions as all the real work gets done elsewhere; they’re wafer-thin and melt in the compiler’s mouth.
Similarly there the destructor isn’t
Again this is to help the compiler inline the calls. As a rule these classes
aren’t used as public bases of other classes, and even if they were those
classes wouldn’t generally be destroyed by a call to the destructor of the
base. Of course if there is any risk of this happening a virtual destructor will
be vital to prevent leaks.
Using an RAII class is simplicity itself. You just create it when you need to use the resource in question. Whenever it falls out of scope the resource will be released without any further action from you.
Because COM uses reference-counting garbage collection COM objects can be used as RAII objects (indeed I said you could use VB6 classes for RAII and internally all VB6 classes “look” like COM even if they aren’t exposed as such).
A good example is the classes provided by calls to the FileSystemObject. File and
TextStream objects will call
Close() when they are destroyed if
necessary, so while RAII is not the primary advantage they give over other
mechanisms (they are simpler than some alternatives, and the only out-of-the-box
way to deal with files in script) they have all the advantages of RAII.
Of course a pointer to a COM object is in itself a resource that must be
carefully managed. If you are using a scripting language or VB6 then the calls to
Release() are managed for you, but in C++
they must be carefully matched. The CComPtr and CComQIPtr templates provided by
ATL use RAII techniques to take much of this burden off the programmer, as is the
_com_ptr_t templates provided by VC++ if you #import without the
The Standard Template Library makes use of the RAII technique in many places. The
file stream templates (std::basic_ifstream, std::basic_ofstream and
std::basic_fstream) for example will close the file stream on destruction if
close() hasn’t yet been called.
RAII is also directly supported by the std::auto_ptr template. Its destructor will call delete on the pointer if it “owns it” (it will for instance stop “owning” a pointer if it is assigned to another std::auto_ptr). Hence it uses RAII to prevent memory leaks.
The runtime performance costs of this technique obviously depend on quite a few factors.
In C++ the cost is typically nil, or an actual improvement in runtime performance over alternative techiques. As these classes have inline non-virtual constructors and destructors the code produced by an optimising compiler should be pretty much identifcal to that of the equivalent using explicit resource acquisition and release. The class will also typically use no memory, or no more than would have been necessary through other methods.
In cases where the code executed between acquisition and release is more complicated, and in particular where there are a wide variety of possible exceptions that could be thrown, this technique is typically more efficient than alternative techniques which involve either checking state or catching exceptions to rethrow them after perfoming the necessary cleanup.
In cases where the acquisition and/or release is itself complicated the runtime complexity generally increases at the same or a slower rate with RAII than with alternative techniques, and at this point efficiency itself is a good reason to consider RAII. It’s worth noting that even if a virtual destructor is needed for an RAII class it is generally possible for the virtual selection mechanism to be performed at compile-time rather than through the vector table (or whatever implementation of virtual functions is used) — since the compiler knows the exact type of the object it does not need to use the virtual mechanism at run-time.
In VB6 the cost is typically greater. All VB6 classes are internally COM classes.
As such they all have implementations of IUnknown (and IDispatch), a reference
count, a degree of thread-safety, are created on the heap, and their creation
and destruction is controlled by virtual calls to
It appears from experimentation that these features are not optimised away by the compiler, certainly not the implementation of IUnknown.
This means that in designing VB6 programs we cannot use RAII quite as freely as in C++. The benefits far outweigh the costs in cases where analysis of the possible conditions is daunting and liable to change, in less complicated cases the decision is more difficult to make.
In all languages the effects of RAII on the readability of code will depend largely on how clear the purpose and semantics of the RAII class is. As I’ll go into in more detail later, an RAII class should be considered a design tool and its use a notational benefit to the function in which it is used.
Because RAII is better known in C++ than VB a class that exists primarily for RAII purposes may seem a bit “foreign” to a VB coder. Alternatively VB coders are generally used to classes cleaning up after themselves completely and will expect RAII behaviour when a class is used to give access to a resource. C++ coders will not necessarily expect this, though at all but the lowest levels of abstraction any class that does not exhibit such behaviour is poorly designed.
There is often a slight increase in the complexity of “stepping through” code in a debugger if you step into the class’s constructor and destructor. However once the class is tested, trusted, and well known these can be stepped over and the task of stepping through code is simplified.
There is generally a run-time performance hit when debugging even in C++. But worries about run-time performance of a debug build are obviously misguided.
The main reason for studying programming idioms and patterns is so that we can consciously use them in our designs. From a design perspective there are two different times when we use RAII.
The first kind of class is one that is designed purely, or at least primarily, to
provide RAII semantics. The STL’s std::auto_ptr template is a classic example of
that. It is small, light and typically calls to its members, including the
constructor and destructor, will be inlined and optimised away to the equivalent
hand-written code. A notable feature of this class is that it does not protect
the user from the effects of derefencing a null pointer. With a more generalised
attempt to protect the user from the possible pitfalls of pointers we would
expect such an attempt to throw an exception. However the standard
([ISO/IEC 14882]) defines
to forbid exception throwing. This is a very low-level class and should be used
appropriately. It’s worth noting that the very fact that RAII can be used in such
low-level situations reflects how efficient the mechanism is.
The second kind of class is one that is designed to give access to a resource at an appropriate level of abstraction, and which provides RAII semantics as they apply to the resource in question in addition to other functionality. Examples would be both the STL and COM methods for dealing with file objects. When these classes are destroyed the close their files.
There is a level of abstraction above which RAII semantics must be provided if there are any resources managed by the class. At high levels of abstraction a programmer should not have to think about the underlying resources, indeed it should be possible to change the nature of those underlying resources without the higher-level code having to be changed. At this point it largely ceases to be RAII and becomes a matter of the basic pre-conditions, post-conditions and invariants of the class’s design.
In these cases the RAII semantics are often not free. For example in the cases of the file classes mentioned all of these classes enable users to close the file before the class is destroyed, so the destructor has to check that the file is open before closing it. Also they are often destroyed via pointers requiring virtual calls to the destructor and so on. However these facts are inherent in the increase in abstraction rather than RAII itself. They are also generally over-estimated by those coders obsessed with efficiency and we should avoid over-stating them. They aren’t free, but they are very cheap.
The important features of pure RAII classes like std:auto_ptr are efficiency, transparency, and reliability.
->is identical to use of a pointer. The ATL win class provides all the Windows API functions, but as member of the class. Often it is appropriate to allow these types to be cast to the types of their underlying representation.
With higher level classes RAII is generally taken for granted by users. Certainly any class that is part of the public interface of a component or library that is not specifically geared towards low-level use should clean up after itself correctly or its design is clearly flawed.
At this level of abstraction the real question is to make sure that the RAII behaviour is reliable and error-free. Often using lower-level RAII classes as part of the internal representation is a good way to do this.
The design of any class should consider the state that exists before and after creation, before and after destruction, and invariants that will exist before and after any function is called on a fully constructed object. RAII helps this design task.
With classes that are not easily labelled “low level” or “high level” design becomes somewhat less clear-cut. Not only is RAII not a given with low level classes, but it is often inappropriate at that level (when a class deals with a resource, but does not “own” it). You should have a good idea of exactly what level of abstraction a class operates at, and should communicate this clearly to the class’s user. A class which doesn’t seem to fit well at a single level of abstraction is almost invariably a poorly-designed class.
RAII classes can be particularly useful when the scope which governs their lifetime is another class. Where this comes into its own is in cases where constructors or destructors can throw exceptions.
In C++ the order of construction is that first the base, and then all member objects are constructed in the order of their declaration. Then the constructor’s body (if any) is executed. This is conceptually recursive, with the same process happening to any member objects of member objects, though with inline constructors this recursion doesn’t really involve the overhead of a function call.
The order of destruction is the opposite, first the body of the destructor is called, then the destructors for member objects, then the destructor of the base.
This means that if a member object with RAII semantics has been created and an exception happens before the constructor has completed then its destructor will be called as part of the stack unwinding. Hence an object which controls multiple resources can guarnatee their cleanup even if it isn’t fully constructed by using member RAII objects.
Further, if there is a dependency between the acquisition and release of two or more resources they generally tend to be nested, that is the first resource acquired must be the last released. This naturally matches the order of construction and destruction of C++ classes.
One thing to be careful of is the danger of throwing exceptions in destructors.
This is generally true whether a class uses RAII or not. If a destructor is being
executed during the process of an exception being thrown (most often because of
stack-unwinding) and it throws an exception then, as always if an exception is
thrown while another exception is currently being thrown,
terminate() will be called.
Some people say that you should never allow an exception to escape a destructor.
This isn’t strictly true, though it is a good principle to follow when possible.
Since an RAII class is designed with exception safety in mind, it is wise to
avoid giving them destructors that can throw exceptions. However the very nature
of their destructors means it is often important that they can throw exceptions.
uncaught_exception() allows a destructor to check if it can
throw an exception without calling
terminate(). However you may
decide that calling
terminate() is appopriate, particularly for
small programs that can signal failure to complete through their return code.
In VB there isn’t the same guarantee about order of destruction of member objects, but the same general principle applies.
We’ve seen how RAII can help the design of individual classes and functions, and what is involved in the design of RAII classes themselves. When looking at the wider scope of a component, library or application RAII stops being about exception-handling mechanisms, and becomes part of the overall exception-management strategy.
RAII answers a lot of cases where some developers avoid conventional exception mechanisms in favour of other techniques (such as returning values indicating failure, or setting global error number variables) with the myriad problems they bring.
RAII adds resiliance to code that cannot know what exceptions may be thrown, in particular code that calls virtual functions, or code that calls functions which depend upon template arguments.
RAII makes it easier to allow exceptions which are the “concern” of other components to pass through them from the component that threw the exception to the component that will catch it. This helps you to guarantee good exception-safe behaviour without knowledge of the exceptional circumstances that will be involved.
A generalisation of the RAII class is the sentry class. A sentry class is any
class which (optionally) performs some action on construction, and then performs
some action on destruction. RAII classes are sentry classes where the sentry
actions acquire and release resources. Sentry classes for dealing with critical
sections and monitors are RAII or not depending on whether you consider critical
sections and monitors to be resources. This generalisation of the RAII idiom
is also called the “Execute Around” pattern, though that term
is also used for other mechanisms (such as the use of
finally in Java).
Implementations of the STL streams use sentry classes to deal with various matters, such as locale-specific whitespace-skipping, management of tied streams, etc. Which must be called before or after certain stream operations in an efficient and flexible manner.
If you thought you didn’t know the RAII idiom when you started reading this, you’re quite likely thinking “Well duh! Everyone does that”. However in giving it a name and examining it will be more readily to hand when you have a design problem it will help solve. RAII is an simple but important example of such.
.NET uses non-deterministic garbage collection, much like Java, and RAII techniques are not directly applicable to .NET.
C# offers the
using keyword (different from
using in C++, although
there is support for using directives, but not for using declarations, in C# as well). A
using statement will cause System::IDisposable::Dispose to be called on the
affected object when it goes out of scope, allowing for some RAII-like functionality.
Managed Extensions for C++ do not offer such a new use for
Nemanja Trifunovic has written
RAII Idiom and Managed C++
which addresses this, and indeed goes beyond what is offered to C# programmers through
using, and I recommend it to all Managed C++ hackers.