Click here to Skip to main content
13,248,562 members (75,854 online)
Click here to Skip to main content
Add your own
alternative version

Stats

28K views
197 downloads
22 bookmarked
Posted 8 Mar 2010

Building a Quick and Handy Reference Counting Class

, 15 Mar 2010
Rate this:
Please Sign up or sign in to vote.
The article describes how to implement reference counting in a C++ class that can be used like any pointer

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.

rcptr01.png

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.

rcptr02.png

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.

rcptr03.png

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.

/**
* Base class for all classes that support reference counting
*/
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)
{
	//1: Demonstrate RCBase class

	//Module 1 creates an object
	MyClass *a = new MyClass();
	a->grab();		//RC=1

	//Module 2 grabs object
    MyClass* ptr = a;
    ptr->grab();    		//RC=2

    //Module 1 no longer needs object
    a->release();      		//RC=1
    a = NULL;

    //Module 2 no longer needs object
    ptr->release();    		//object will be destroyed here
    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:

/**
* A reference counting-managed pointer for classes derived from RCBase which can
* be used as C pointer
*/
template < class T >
class RCPtr
{
public:
	//Construct using a C pointer
	//e.g. RCPtr< T > x = new T();
	RCPtr(T* ptr = NULL)
		: mPtr(ptr)
	{
		if(ptr != NULL) {ptr->grab();}
	}

	//Copy constructor
	RCPtr(const RCPtr &ptr)
		: mPtr(ptr.mPtr)
	{
		if(mPtr != NULL) {mPtr->grab();}
	}

	~RCPtr()
	{
		if(mPtr != NULL) {mPtr->release();}
	}

	//Assign a pointer
	//e.g. x = new T();
	RCPtr &operator=(T* ptr)
	{
		//The following grab and release operations have to be performed
		//in that order to handle the case where ptr == mPtr
		//(See comment below by David Garlisch)
		if(ptr != NULL) {ptr->grab();}
		if(mPtr != NULL) {mPtr->release();}
		mPtr = ptr;
		return (*this);
	}

	//Assign another RCPtr
	RCPtr &operator=(const RCPtr &ptr)
	{
		return (*this) = ptr.mPtr;
	}

	//Retrieve actual pointer
	T* get() const
	{
		return mPtr;
	}

	//Some overloaded operators to facilitate dealing with an RCPtr 
         //as a conventional C pointer.
	//Without these operators, one can still use the less transparent 
         //get() method to access the pointer.
	T* operator->() const {return mPtr;}		//x->member
	T &operator*() const {return *mPtr;}		//*x, (*x).member
	operator T*() const {return mPtr;}		//T* y = x;
	operator bool() const {return mPtr != NULL;}	//if(x) {/*x is not NULL*/}
	bool operator==(const RCPtr &ptr) {return mPtr == ptr.mPtr;}
	bool operator==(const T *ptr) {return mPtr == ptr;}

private:
	T *mPtr;	//Actual pointer
};

Our new class can now be used as follows:

//Module 1 creates an object
RCPtr< MyClass > a2 = new MyClass();	//RC=1
	
//Module 2 grabs object
RCPtr< MyClass > ptr2 = a2;		//RC=2
    
//Module 2 invokes a method
ptr2->print();
(*ptr2).print();

//Module 1 no longer needs object
a2 = NULL;      			//RC=1
    
//Module 2 no longer needs object
ptr2 = NULL;    			//object will be destroyed here

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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Ahmed S. Hefny
Instructor / Trainer Cairo University
Egypt Egypt
No Biography provided

You may also be interested in...

Pro
Pro

Comments and Discussions

 
GeneralConcepts for reference counting Pin
Stefan6316-Mar-10 0:23
memberStefan6316-Mar-10 0:23 
Questionpossible bug? Pin
david garlisch15-Mar-10 15:10
memberdavid garlisch15-Mar-10 15:10 
AnswerRe: possible bug? Pin
Ahmed S. Hefny15-Mar-10 19:34
memberAhmed S. Hefny15-Mar-10 19:34 
GeneralRe: possible bug? Pin
david garlisch15-Mar-10 19:45
memberdavid garlisch15-Mar-10 19:45 
GeneralIncrement/Decrement Pin
basementman10-Mar-10 4:57
memberbasementman10-Mar-10 4:57 
GeneralRe: Increment/Decrement Pin
Ahmed S. Hefny11-Mar-10 9:39
memberAhmed S. Hefny11-Mar-10 9:39 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.171114.1 | Last Updated 16 Mar 2010
Article Copyright 2010 by Ahmed S. Hefny
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid