Click to edit Master text styles
What exactly is bad about this
code? The fact that the programmer has to work hard:
1.When creating it,
remember to always match the acquisition and release calls.
2.When reviewing, check
for the matching calls
3.Think non-locally. The
release might be far away from the acquisition
4.Make sure that nothing
non-linear (a fork in the execution) happens between the two calls
Non-locality and non-linearity
are the major problems faced by programmers. The infamous gotos were banished
exactly for this reason. Structured programming linearized and localized
procedural programming. RM does the same for resources.
Simple example: a pointer points to a resource.
Is the pointer valid?
Should this pointer be deleted?
Do all execution paths lead to the deletion?
Are other pointers pointing to the same resource?
Do they delete it?
Do any paths of execution lead to double deletion?
Introducing the smallest flow of control modification, arbitrarily far from
pointer manipulations, might lead to the resource leaking or double
This is how you can write “exception safe” code in C.
This code is not readable—a lot of bookkeeping. It is also incorrect. Consider
what happens when an exception is thrown between the two mallocs. The freeing
of p2 will result in unexpected behavior. Correct code should start with:
char * p1 = 0;
char * p2 = 0;
A lot of programmers who switch from C to
C++ write this kind of unmaintainable code. It is even worse than the original
C, because there is no analog of “finally” in C++.
This is how you encapsulate the critical
section. This class should be part of your library. The correctness of
resource management in this class is pretty obvious to a C++ programmer. It’s
Again, a good candidate for your library.
Proof of correctness, a no-brainer.
Compare this with previous “exception
safe” attempts. This is an exception safe no-brainer. The code after the lock
is constructed can be modified in crazy ways: returns, gotos, exceptions.
Nothing will break its “resource correctness.”
Normally the critical section object is embedded in some higher-level object that
An object (instance of a class) is a
resource, so are its sub-objects and its base class sub-objects, and so on,
Notice that Memory is the only resource that is managed by generic garbage collectors.
Heap objects are sort-of managed, in the sense that their eventual
Progress UI is an interesting case. An object that switches mouse cursor to hourglass
in its constructor and switches it back in its destructor. Gives you a guarantee
that after the command is executed (successfully or not), the cursor will go
back to normal. Similar solutions apply to progress bars, “busy” displays in
the status bar, etc.
A scope owns all its local variables
(including function arguments within the scope of the function).
object owns its member data, which may be objects themselves, recursively.
These two types of ownership guarantee resource release:
When a scope is exited, all local variables are destroyed. Stack unwinding make
this process exception safe
When an object is destroyed, all its data
members (and the base class part) is destroyed.
Note: We are assuming that
no sane C++ programmer writes classes whose destructors may throw exceptions.
Every resource is owned by an object.
Notice the locality of the proof of correctness: it’s enough to check
the constructors and the destructors (which usually are defined next to each
Normally this is done using auto_ptrs.
According to the First Rule of
Acquisition, a resource is always allocated in a constructor of an object
whose destructor deallocates this resource. So all resources are encapsulated
in objects. These objects can live in local scopes, global scopes, or be
sub-objects of other objects. Their destruction is guaranteed. If an object is
allocated from the heap, its destruction is also guaranteed, because it can
only live inside its owner object.
Looking at code written using the RM precepts, you can easily prove its resource
correctness. Just check that all resources are allocated in constructors, and
that the corresponding destructors deallocate them. That’s all.
These rules will now be relaxed without invalidating our argument.
Very few programs can be written without
resources being transferred between scopes.
As before, let’s look at built-in mechanisms first.
Objects are resources, how are they transferred?
Return by value transfers an object from function scope to the caller’s
Passing by value transfers an object (non-destructively) in the opposite direction.
Both of these use a combination of the copy constructor and operator=.
Those can be overridden, giving room for different transfer policies.
Passing “by value” between scopes. The
resource to be passed is encapsulated in a lightweight transfer object with
value-like semantics. By defining the appropriate copy constructor and by
overriding operator=, the transfer object can implement various ownership
By declaring these two private, you can disallow transfer.
Move semantics, a la auto_ptr.
A small subset of RM dealing with heap
resources makes garbage collection unnecessary. Realistically, auto_ptr
provides adequate transfer policy.
Since auto_ptr is a template, we can’t
really follow the First Rule of Acquisition. Instead of allocating the
resource in its constructor, we allocate it
• as an expression passed directly to auto_ptr
• as an expression that is the argument to
To transfer the ownership we pass auto_ptr by value to and from
Note: Don’t pass auto_ptr by reference—it’s confusing. If you don’t want to pass
ownership, get the pointer from auto_ptr and pass it around.
auto_ptr<Foo> foo (new Foo);
UseFoo (foo.get ()); // no ownership passed
The standarizers took great pains to
prevent the possibility of using auto_ptr as template parameters to standard
containers. Move semantics could easily break a standard container, which
thinks nothing of making temporary copies.
Reference counted objects are ok with standard containers. For instance, Alexandrescu’s
smart pointers or Boost smart pointers will work correctly.
Notice that syntactically a container of smart pointers looks like a container
of pointers, so you have to get used to it. For instance, when you dereference
an iterator, you get a pointer. You have to use special pointer-accepting predicates
for searching and other standard algorithms.
The biggest inconvenience of this approach is that you essentially have to use
ref-counted pointers everywhere you would normally use auto_ptrs. What if you
are a fan of auto_ptrs? They are smaller, faster, and cooler.
To store heap object use my auto_vector.
What if you need an auto_set or some such? You can write one, or you can separate
ownership from set-ness. Keep heap objects in auto_vector and store non-owning
pointers to them in a standard set.
There is an example of this on our web site www.relisoft.com/resource. It implements
topological sort using this approach.
Push_back doesn’t strictly follow
exception-safety requirements. If it fails, it still modifies its argument
(deletes the pointer). I would argue that this is very reasonable behavior in
the case of auto_ptrs. They are supposed to release their resource in
the case of failure.
Pop_back syntax differs from the ususal pop_back
syntax, which is not supposed to return anything. Again, this makes perfect
sense for auto_ptrs. The (rather unconventional) division of responsibilities
between pop_back() and back() in standard containers is done in the name of
exception safety. Here, instead, the conventional pop syntax is exception
safe. Incidentally, auto_vector::back() returns a pointer, not auto_ptr.
This is a clever trick to allow index
access to auto_vector to be used as an lvalue (the left-hand side of the
assignment). Ownership of the new object has to be transferred to auto_vector.
The old entry should be released in the process.
This is done by
overloading operator to return a helper object that serves as an lvalue
This object keeps a reference to a
pointer, so it can not only modify (in this case, delete) the object pointed
to, but also modify the pointer itself. Notice that auto_lvalue has
pointer-like behavior. This, for instance, makes such code possible:
autoVec ->Method ();
You may also want to prevent the incorrect use
delete autoVec ;
by overriding operator delete.
Following these few principles you get
“controlled” garbage collection. You not only have the guarantee of
destruction, but you also know when the destruction takes place.
Completely, utterly useless. Not only
that, languages with GC usually don’t offer other mechanism that can be used
to implement RM (Java, C#), so you are back to using the ugly try/catch type
Windows uses many types of handles. We
encapsulate all of them in this template. NativeHandle is the original Windows
type, like HWND, HDC, HPEN, etc.
Note that Handle is-a NativeHandle not by
way of inheritance, but through the overloading of the cast operator. Use this
trick when the underlying type is not a struct or class (here NativeHandle is
a pointer to struct).
This generic handle is not a resource yet—it
corresponds to a naked pointer in the auto_ptr lingo.
This is the analog of auto_ptr.
AutoHandle is-a BaseHandle. BaseHandle can be the generic Handle, or it can be
derived from Handle, adding specific functionality (examples later).
types of handles are disposed of differently. We encapsulate it inside DisposalPolicy,
a la Alexandrescu.
These two member functions define
AutoHandle’s move semantics. Transfer policy is similar to that of
auto_ptr—there’s always only one owner.
There are additional subtleties
that are needed for return by value to work. A helper (template) class
auto_handle_ref is used the same way auto_ref is used with auto_ptr.
This is the default DisposalPolicy. It
calls the specific Dispose method that has to be defined for each BaseHandle
by specializing this template.
Simple example. BaseHandle is a typedef
of generic Handle<HICON>. Disposal policy is specialized for this
This is a more involved example.
Gdi::Handle is a generic GDI handle. All GDI handles are disposed using the
same API DeleteObject.
HPEN is a specific example of a GDI
handle. Pen::Handle is-a Win::Handle<HPEN>. Pen::AutoHandle uses the GDI
disposal policy, common for all GDI objects (brushes, bitmaps, etc.).
This is an example of how Pen::AutoHandle
is used to manage the pen resource. The Pen::Maker object gathers data needed
for the creation of a pen (there are more options than just width). The Create
call allocates the resource, puts it immediately inside an owner/transfer
object and returns it by value. The caller becomes the owner. Notice that the
caller is forced to use a Pen::AutoHandle to receive the result of Create.
There is no chance of leaking the pen at this point.
This is a different example of RM. The
base class Canvas encapsulates a handle to a device context. Its member
functions encapsulate all the APIs that take HDC as their main argument.
This is an analog of a naked pointer.
There are several owner classes with
different acquisition/release methods. This one is created in the context of
WM_PAINT message. Notice the no-transfer policy (private copy-co and
This one is created outside WM_PAINT to
update a window.
Each call to new must immediately pass its result to auto_ptr.
There should be no explicit calls to delete.
Every class with virtual member functions must have a virtual destructor.