Sunday, October 7, 2012

Reference counted classe in C++

My inspiration for Smart pointers and reference counted classes come from Webkit, time when i started hacking into webkit source code :)

Although i have tried to remain much simple and easier than in original webkit implementation of reference counted classes, but you will see most of code here resembles with webkit.

The idea of reference counted is very simple, every object  maintains a count(or we can say ref count) of number of pointers pointing towards itself, and as long as the count remains greater than zero, the object is valid and the moment its count becomes zero it is automatically deleted.
The object implemented  a pair of methods ref() and deref(), ref() method increments its count by one and similarly deref() decrements its count by one. Every call to ref() must be balanced by a call to deref().
Every time a new smart pointer points towards the object its ref count is incremented by one(via ref() method), and every time a smart pointer is destroyed the object ref count is decremented by one(via deref() method).
Ref counted objects must be wrapped inside a special smart pointer(RefPtr) in order to properly use and take benefit from reference count implementation.

Let us first talk about RefCounted class and then we will talk about RefPtr.

RefCounted is simply a template class, which has only a single instance member, i.e m_refCount which is the count i have talked above.


template <typename T> class RefCounted {
public:
    virtual ~RefCounted()
    {
    }
    void ref()
    {
        ++m_refCount;
    }
    bool deref()
    {
        if (m_refCount == 1)
            return true;
        
        --m_refCount;
        return false;
    }
protected:
    RefCounted()
    : m_refCount(0)
    {
    }
private:
    // Making it non-copyable,so that it cannot be used as POD
    RefCounted(const RefCounted&)
    {
    }
    RefCounted& operator=(const RefCounted&)
    {
    }
    
    unsigned int m_refCount;
};


Fairly straight forward implementation.

Now let us see RefPtr class implementation

template<typename T> class RefPtr {
public:
    RefPtr()
    : m_ptr(0)
    {
    }
    RefPtr(T* ptr)
    : m_ptr(ptr)
    {
        checkAndRef(m_ptr);
    }
    RefPtr(const RefPtr& o)
    : m_ptr(o.m_ptr)
    {
        checkAndRef(m_ptr);
    }
    template <typename U> RefPtr(const RefPtr<U>& o)
    : m_ptr(o.get())
    {
        checkAndRef(m_ptr);
    }
    ~RefPtr()
    {
        checkAndDeref(m_ptr);
    }
    T& operator*() const
    {
        return *m_ptr;
    }
    T* operator->() const
    {
        return m_ptr;
    }
    T* get() const
    {
        return m_ptr;
    }
    
    RefPtr& operator=(const RefPtr& o);
    RefPtr& operator=(T* optr);
    
    template<typename U> RefPtr& operator=(const RefPtr<U>&);
    
    void clear();
    void swap(RefPtr&);
    
private:
    T* m_ptr;
};


template<typename T> inline RefPtr<T>& RefPtr<T>::operator=(const RefPtr<T>& o)
{
    T* optr = o.get();
    checkAndRef(optr);
    T* ptr = m_ptr;
    m_ptr = optr;
    checkAndDeref(ptr);
    return *this;
}
template<typename T> inline RefPtr<T>& RefPtr<T>::operator=(T* optr)
{
    checkAndRef(optr);
    T* ptr = m_ptr;
    m_ptr = optr;
    checkAndDeref(ptr);
    return *this;
}
template<typename T> template<typename U> inline RefPtr<T>& RefPtr<T>::operator=(const RefPtr<U>& o)
{
    T* optr = o.get();
    checkAndRef(optr);
    T* ptr = m_ptr;
    m_ptr = optr;
    checkAndDeref(ptr);
    return *this;
}


Every time RefPtr takes in a new object it calls ref() on it through checkAndRef() and when RefPtr is destroyed it calls deref() through checkAndDeref() in its destructor. Rest of methods are for general usability.

In this class two important functions are used checkAndRef() and checkAndDeref(), here is the implementation of these functions

template<typename T> void checkAndRef(T* ptr)
{
    if (ptr != 0)
        ptr->ref();
}
template<typename T> void checkAndDeref(T* ptr)
{
    if (ptr != 0)
        if (ptr->deref())
            delete ptr;
}

In order to use RefCounted we must inherit from it.

Let us have a class "Foo" and we want to make it reference counted class. So the first step is to inherit "Foo" from "RefCounted"


class Foo : public RefCounted<Foo> {
public:
    static RefPtr<Foo> create()
    {
        RefPtr<Foo> foo(new Foo());
        return foo;
    }
    virtual ~Foo()
    {
    }
    // Rest of Foo public implementation
private:
    Foo()
    {
    }
    
    // Making it non-copyable,so that it cannot be used as POD
    Foo(const Foo& o);
    Foo& operator=(const Foo& o);
    
    // Rest of Foo private implementation
};


We have provided a public static factory method for creation of Foo instance and make its constructor private, in this we have make it sure the object is always created and wrapped inside RefPtr before returning and it is automatically deleted upon destruction.
Also we have made this class non-copyable so that it cannot be used as POD(Plain Old Datatype).

In main() method(or any other place), we may use the whole implementation of RefCounted, RefPtr as follows


void funA(RefPtr<Foo> arg)
{
    // do whatever want to do with arg
}
int main(int argc, const char * argv[])
{
    RefPtr<Foo> obj = Foo::create();
    funA(obj);
    
    return 0;
}


Now let us examine in detail what happens to m_refCount, during whole execution of this code block.

  1. First static create() method is called.
  2. Inside create() and instance of Foo is created through "new" operator, and ref count is zero here.
  3. The newly created object is immediately wrapped inside RefPtr(foo), and its ref() method is called which increments its ref count by one, now its ref count is one.
  4. Returned object is created(it is an anonymous object), which calls ref() and its ref count goes to two.
  5. Local smart pointer object "foo" is destroyed when control returns from "create()" method which calls deref() and ref count goes to one.
  6. "obj" is crated which calls ref() and ref count goes to two.
  7. Returned anonymous object is destroyed which calls deref() and ref count is one.
  8. Argument object(arg) is created (to pass to function funA()) which calls ref() and ref count goes to two.
  9. When control returns from funA() the "arg" object is destroyed which calls deref() and ref count goes to one.
  10. When control returns from main() the local object "obj" is destroyed and underlaying wrapped "Foo" object is deleted.
On some compilers the during optimization stage some of above steps may be optimized away.


Issues

  • The above implementation of RefPtr will introduce reference count churn, it is even more if same object is passed as argument to a method/function and it returns the same object. One possible solution is to use another smart pointer namely PassRefPtr(more on this in my next blog when i talk about object ownership).
  • Above implementation RefCounted is not thread safe, it is potentially dangerous to pass a RefCounted object to other thread or return it from other thread. It may corrupt our m_refCount instance variable and race conditions may arise. 

Complete files can be downloaded from RefCounted.h, RefPtr.h & Foo.h

Sunday, September 9, 2012

Smart Pointers in C++

Hi this is my first post in a series of posts on memory management in C++.

Why smart pointers?

Suppose we have a code like


class A {
public:
    A();
    void someMethod();
    void someOtherMehtod();

    // .....
};
void fooBar(A* a);
void foo()
{
    A* a = new A();
    a->someMethod();

    // do something with a
    fooBar(a);
    delete a;
}
This is a simple a straight forward code, we are creating an instance of "A", passing "a" as an argument to "foo" function and then deleting it. Pretty nice until now :)

But their is a potential memory leak here. In order to release memory obtained through "new" operator we must use "delete" operator, otherwise memory will leak. Suppose if we do not remember to call "delete" or suppose inside "fooBar()" implementation an exception occurs, then control will never return back and "delete a;" is never executed and we will be leaking memory :(
Same is true when you use any third party API, it can also raise an exception. Remember we are only sure about our own code, we cannot guarantee any one else code.

The simple solution is to use "auto_ptr"(a smart pointer) provided by C++ standard library.
A simple "auto_ptr" class might look like


template<typename T> class auto_ptr {
public:
    explicit auto_ptr(T* t = 0)
        : m_ptr(t)
    {
    }
    virtual ~auto_ptr()
    {
        delete m_ptr;
    }
    T& operator*()
    {
        return *m_ptr;
    }
    T* operator->()
    {
        return m_ptr;
    }
    T* get() const
    {
        return m_ptr;
    }
    // some other methods
private:
    T* m_ptr;
};


As you can see "auto_ptr" is just a wrapper around a raw pointer. It guaranties object deletion when it goes out of scope.
Now in order to use "auto_ptr", our implementation of "foo()" will look like

void foo()
{
    auto_ptr<A> a(new A());
    a->someMethod();
    // do something with a
    fooBar(a.get());
}
Or we can use auto_ptr provided by C++ standard library

void foo()
{
    std::auto_ptr<A> a(new A());
    a->someMethod();
    // do something with a
    fooBar(a.get());
}

As you can see their is no need for "delete" here, auto_ptr guarantees us it will call "delete" operator no matter what happens inside fooBar().
Another advantage is we can still use dereference operator with smart pointers just like with raw pointer. Because it has overloaded "*" and "->" operators inside "auto_ptr" class.
"auto_ptr" is the most simplest kind of smart pointers. Their are many more available, which have their own memory management policy.

Why to use smart pointers?

It makes our code less buggy.

void foo()
{
    A* a1 = new A();
    A* a2 = a1;
    a1->someMethod();
    delete a1;
    a1->someOtherMehtod(); // Ohhh a1 is dangling, and we will get a crash here
    a1 = NULL; // a1 is now no more dangling
    a2->someOtherMehtod(); // a1 is still dangling here
}
It is safe to use, it automatically initialises its instance member through default constructor.

It make our code partially garbage collected, we no more need to call delete, it does this for us :)

I will talk about more on smart pointers with introduction to some other kinds available.