Introduction
Although C++ is an excellent choice to combine a modular object-oriented design with high speed and control, it usually gets tedious when it comes to memory management. In complex systems, extreme care has to be taken to avoid both memory leaks and attempts to access unallocated memory. Even simple memory management techniques can be of great help in this case. In this article, I describe reference counting, one of the simplest memory management techniques that is sufficient for many cases. I will explain how it can be implemented in C++ and how to wrap it in a simple pointer class that "does the job" of enabling a managed-alike programming style without too much coding hassle or need for a 3rd party library.
Background
The concept of reference counting is simple; each object maintains a reference counter (RC) that holds the number of pointers pointing to that object. This counter is updated whenever a new pointer points to the object or a pointer pointing to the object is changed. When this counter reaches 0, it indicates that the object is no longer accessible by the program and hence should be deallocated.
Reference counting will do fine as long as you avoid reference cycles. If reference counting managed objects point to each other in a cycle, you will end up with a cluster of objects that are not accessible by the program but having their reference counts higher than 0, thus causing a memory leak.
The straightforward solution to reference cycles is to classify pointers into strong pointers and weak pointers. Weak pointers do not affect the reference counters of the objects they point to. You should be careful in your design to ensure that no cycle of strong pointers can be formed.
Implementing Reference Counting
To implement reference counting in C++, we need to define a class that maintains a reference counter, supports incrementing and decrementing that counter and destroys and deallocates itself when its counter reaches 0.
class RCBase
{
public:
RCBase() : mRefCount(0) {}
virtual ~RCBase() {}
void grab() const {++mRefCount;}
void release() const
{
assert(mRefCount > 0);
--mRefCount;
if(mRefCount == 0) {delete (RCBase *)this;}
}
private:
mutable int mRefCount;
};
Notice that I declared mRefCount
as mutable so that it can be changed by the const
methods grab
and release
, allowing reference counting to operate even on pointers to constant objects. All classes for which we want to use reference counting must be derived, either directly or indirectly, from RCObject
. Usage of this class is simple; to maintain a strong pointer on an object of class MyClass
, simply point to it by a MyClass*
and invoke grab
. Once you are done with that object, invoke release. The object will delete itself if it is no longer pointed to somewhere else. Notice that the reference counter is initialized to 0 which means that when you create a new object, you have also to grab it. This will be convenient when we develop our pointer. The following code example demonstrates RCBase
:
class MyClass : public RCBase
{
public:
~MyClass() {cout << this << " is no longer needed" << endl;}
void print() {cout << "Hello" << endl;}
};
int main(void)
{
MyClass *a = new MyClass();
a->grab();
MyClass* ptr = a;
ptr->grab();
a->release(); a = NULL;
ptr->release(); ptr = NULL;
}
Usually, an object will be grabbed in the beginning of a function and released at the end of it, or it will be grabbed in the constructor of the pointing object and released in its destructor. You will find base classes similar to RCbase
in frameworks that use reference counting such as Microsoft COM and Irrlicht engine. Although the framework established so far simplifies memory management, there are still two problems:
- You might simply forget to grab or release an object.
- If an object is grabbed in a function, it might not be released if, for example, the function is prematurely terminated due to an exception. We thus need a framework that "automatically" grabs and releases objects as needed and also releases all objects grabbed in a function whenever it terminates.
We can adapt the auto_ptr
class provided by standard C++ library. auto_ptr
is a strong pointer that, whenever changed or destroyed, destroys the object it was pointing to. It is thus useful when it is guaranteed that no object will have more than one strong pointer pointing to it simultaneously. All we need to do is to relax auto_ptr
class such that when it is changed, it releases the object it was pointing to and grabs the object it is going to point to. This results in a simple template class:
template < class T >
class RCPtr
{
public:
RCPtr(T* ptr = NULL)
: mPtr(ptr)
{
if(ptr != NULL) {ptr->grab();}
}
RCPtr(const RCPtr &ptr)
: mPtr(ptr.mPtr)
{
if(mPtr != NULL) {mPtr->grab();}
}
~RCPtr()
{
if(mPtr != NULL) {mPtr->release();}
}
RCPtr &operator=(T* ptr)
{
if(ptr != NULL) {ptr->grab();}
if(mPtr != NULL) {mPtr->release();}
mPtr = ptr;
return (*this);
}
RCPtr &operator=(const RCPtr &ptr)
{
return (*this) = ptr.mPtr;
}
T* get() const
{
return mPtr;
}
T* operator->() const {return mPtr;} T &operator*() const {return *mPtr;} operator T*() const {return mPtr;} operator bool() const {return mPtr != NULL;} bool operator==(const RCPtr &ptr) {return mPtr == ptr.mPtr;}
bool operator==(const T *ptr) {return mPtr == ptr;}
private:
T *mPtr; };
Our new class can now be used as follows:
RCPtr< MyClass > a2 = new MyClass();
RCPtr< MyClass > ptr2 = a2;
ptr2->print();
(*ptr2).print();
a2 = NULL;
ptr2 = NULL;
Now, you no longer have to worry about grabbing and releasing objects. Even if the lines a2 = NULL
and ptr2 = NULL
were not present, the object will be correctly destroyed at the end of the scope of a2
and ptr2
. Your only concern should be when to use a strong pointer (RCPtr
) and when to use a weak pointer (traditional C pointer).
History
- 03/09/2010 - Initial submission
- 03/16/2010 - Fixed a bug that caused access violation when assigning a pointer to a
RCPtr
already pointing to the same object