Simplifying Memory Management
Often programmers face a quandary. They would like to pass objects by value, because it’s simpler than passing pointers or references. When passing pointers or references, they have to worry about how the object is being shared throughout the application and where and when the object should be deleted. Worse, when passing a pointer to a graph of objects, the party responsible for deleting the objects may not have access to all the pointers necessary to do so, and even if it did, object graphs with arbitrary pointer relationships require great care not to delete the same pointer twice. None of this is a concern when passing objects by value.
Passing by value presents its own problems, however. It can be expensive. A heavyweight object can be orders of magnitude more expensive to copy than a simple pointer, which can usually be passed in a register. It can also be just plain wrong to pass by value. Sometimes it’s important for the receiver of an object to be using the same instance as the sender, not a copy (when passing a stream, for example).
The handle-body idiom is an elegant solution for side-stepping this trade-off. With handle-body, you get the simplicity of value semantics with the efficiency and sharing of pointer semantics. This is achieved by putting lightweight handles in the public interface, which the user can pass by value. Each handle points to a body that holds the state and does the real work of the object. The methods on the handle are typically inline calls that simply forward the request to the body and return the result. The body also maintains a reference count to keep track of the number of handles pointing to it. As a handle is passed around an application, all copies of the handle point to the same body, with each copy increasing the reference count. When a handle goes out of scope, it decrements the body’s reference count. If the count goes to zero, the body is deleted. Thus, the body is guaranteed to live as long as necessary, but no longer. This memory management is completely transparent to the user of the handle.
Be warned that the internal reference count, which you can only see by peering into a body object with a debugger, is one less than the number of handles pointing to the body. Thus, a body referenced by a single handle has an internal count of 0, a body shared by two handles has an internal count of 1, and so on.
NOTE: Member functions returning a body’s reference count return the actual number of handles referencing the body, not the internal count shown in the debugger.
The diagrams in this chapter show the logical reference count — the number of handles referencing the body — not the internal private state you see in a debugger.