Sunday, 8 July 2007

Smart Pointers

"Smart Pointers" Why do we need it?

It behaves like a pointer but does more than that. If used properly, it can enhance the code robustness, reduce memory leak headaches. 

What more does it do?

Consider a scenario, where we  use dynamic memory allocations extensively. We need to keep track of each allocation we did, for freeing it back to the system after its usage. if we forget, sure there will be a leak.

void SomeFunction()
{
    CSomeClass *ptr = new CSomeClass();

    ...

    ptr->MemberFunction();

    ...

    return; // Usual mistake! forgot to free memory. Leak!
}

Consider another case, where we allocated memory dynamically but the statement next to that thrown an exception. It contributes for a memory leak, unless the exception is caught and the memory is freed.

void SomeFunction()
{
    CSomeClass *ptr = new CSomeClass();

    ...

    ptr->MemberFunction(); // If this function throws exception, memory pointed by ptr won't be freed unless this is caught and freed

    ...
}

One more case for analysis,

CSomeClass* ptr = new CSomeClass();
CSomeClass* otherptr = ptr;

...

ptr->SomeFunction();

...

delete ptr;
ptr = NULL;    // ptr - used and safely deleted

...

otherptr->SomeFunction();   // Dangerous! otherptr is dangling

All these can happen with developers' small negligence. Using Smart pointer can avoid such nasty situations.

How?

Let's have a look at auto_ptr.

auto_ptr is one of the smart pointer templated class in Standard C++ library. It encapsulate a pointer. To embed any primitive or user defined pointer type, it has been made a templated class.

template<class T>
    class auto_ptr
    {

public:

    auto_ptr(T *p = 0);    // Constructor
    auto_ptr(const auto_ptr<T>& Y); // Copy constructor
    ~auto_ptr()    // Destructor   
    {
        delete ptr;  // free the memory
    }

    ...

private:

    T *ptr;
    };

On executing the constructor of this, it takes the ownership of the  memory pointed by the pointer. Since the instance of the auto_ptr is crated on stack, the developer need not worry about freeing the memory.

void function()
{
    auto_ptr<CSomeClass> smartPtr(new CSomeClass());
    // Use smartPtr

    ...

    // No need to worry about freeing the memory
    // When the SmartPtr goes out of scope, will call the auto_ptr's destructor which will in turn delete the embedded ptr
}

Even if there is an exception thrown before cleaning up, stack-unwinding will initiate the auto_ptr's destructor which will in turn free the memory.

Next question is how will we be able to use the smart pointer as a pointer. I mean we should be able to do the following things normally as we were doing with mere pointers,

smartPtr->SomeFunction();
*smartPtr = 3;

auto_ptr has few more overloaded functions, which facilitates us to achieve this.

T& operator*() const
{
    return *ptr;
}

T *operator->() const
{
    return ptr;
}

These two operator overloaded functions enable us to use smartPtr as regular pointer. operator *() will help in dereferencing the pointer and operator->() will do the indirection job.

Article about auto_ptr at gotw

2 comments:

Anonymous said...

What is wrong with this statement? std::auto_ptr ptr(new char[10]);

Prashanth Narayanaswamy said...

@Ibrahim

Good question!

Have a look at this article for more details on this problem,
Dont Ever Mix Arrays with auto_ptr

-Prashanth