This post is part of an ongoing series about Affordances and Programming Languages.

When working in an object-oriented programming language, it is important to consider object lifetime. That is, how does an object get created, and how does it get destroyed or cleaned up when we’re done with it? There’s also the related problem of releasing scarce resources such as file handles, socket connections, and locks/mutexes.

In an ideal world with unlimited resources, we wouldn’t have to worry about cleaning up after ourselves. We could just create objects and acquire resources and never worry about it again. Unfortunately, we don’t live in that kind of world, and so we need to be concerned about cleaning up after ourselves.

In C, this is all done manually. We need to ensure that we pair malloc() and free(), fopen() and fclose(), etc. This can get tedious and error prone.

Many languages today have automatic garbage collection, first introduced by John McCarthy for Lisp. For many years, garbage collection was considered too slow for real-world use, and so was not widely adopted. Today, that has changed and almost every language has it.

Automatic garbage collection is nice, but only deals with memory allocation. What about other kinds of resources? Those still require the manual pairing of acquisition and release.

In languages with blocks, like Smalltalk and Ruby among others, we can implement helper methods that acquire the resource, invoke a block, and then release the resource. For example:

Locks in Smalltalk
lock := RecursionLock new.
lock critical: ["code which must be in a critical section"]
Files in Ruby'myFile') do |file|
# work with myFile's contents

Another option is to use an ensure or finally construct to achieve the same goal:

Files in Smalltalk
rs := 'myFile' asFilename readStream.
["work with myFile's contents"] ensure: [rs close].

C++ does not have automatic garbage collection (yet), but does have something that very few languages have: deterministic destruction semantics. In garbage-collected languages, we don’t have control over when (or even if) an object will be finalized by the garbage collector. However, in C++ we know exactly when that will happen.

Determinstic destruction is an affordance that enables the poorly-named but very useful Resource Acquisition Is Initialization (RAII) idiom. With RAII, we define a class that acquires the resource in its constructor and releases it in its destructor. Thus, we can tie the lifetime of the resource to the lifetime of an object. Because we know exactly when the object will be destroyed, we can control when we will release the resource.

Modern C++ makes heavy use of RAII. The standard library’s smart pointers (std::unique_ptr and std::shared_ptr), file streams, and mutexes are prime examples of this.

void f()
std::unique_ptr<MyClass> p(new MyClass(1, 2, "foo"));
// Use p

At the end of f(), p is automatically released by its destructor, freeing up any memory allocated. This happens when f() exits normally, but also if there is an early return or if an exception is thrown.

Consistent use of RAII in C++ makes code exception safe and eliminates most instances of resource leakage.

In today’s code, manual memory management is almost an anachronism. However, we often forget that automatic garbage collection doesn’t deal with other kinds of resources, and so we end up using different patterns for other resources than we do for memory management. RAII allows us to use exactly the same patterns and idioms for all resources, including memory.