Tuesday, 18 November 2014

Smart Pointers implementation in C++ with examples

Smart Pointers implementation in C++ with examples:

A smart pointer is a pointer that is used to handle the problems that are caused by using the normal pointers.

class MyStudentCLs
{
    int iAge;
    char* pName;
    public:
        MyStudentCLs(): pName(0),iAge(0)
        {
        }
        MyStudentCLs (char* pName, int age): pName(pName), iAge(age)
        {
        }
        ~MyStudentCLs ()
        {
        }
        void Display ()
        {
            printf("Name of the student= %s Age of the student = %d \n", pName, iAge);
        }
        void Shout ()
        {
            printf("I am at the shout function");
        }
};
int _tmain (int argc, _TCHAR* argv[])
{
  MyStudentCLs* pMyStudentCLs  = new MyStudentCLs ("Suve", 15);
  pMyStudentCLs->Display ();
  delete pMyStudentCLs;
  getchar ();
  return 0;
}
The output of the above program is
Name of the student= Suve Age of the student = 15

Here we need to delete the object that is used dynamic memory using delete. If we forgot to delete the object, we will get the memory leak. The main problem that comes with the pointers is memory management.
To overcome these types of problems that are caused by pointers, smart pointers are used.
Bu using smart pointers, we can make pointers to work in way that we don’t need to explicitly call delete.
Smart pointer is a wrapper class above a pointer with operator like * and -> overloaded.The objects of smart pointer class look like pointer, but can do many things that a normal pointer can’t like automatic destruction, reference counting and more.Here we need to create the pointer object of the class, because the destructor and the overloaded operators
* and ->
are called automatically when the object goes out of scope, the memory allocated for that object automatically deleted.
Let us look into the small example to understand more about the smart pointers
#include<iostream>
using namespace std;
class MySmartPtrCls
{
   int *pcPtr;  // Actual pointer
public:
   /* Constructor of the class */
    MySmartPtrCls (int *iPtr = NULL) {
      cout << "I am in the constructor of the smart pointer class" << endl;
      pcPtr = iPtr;
    }
     /* Destructor of the class */
   ~MySmartPtrCls() {
     cout << "I am in the destructor of the smart pointer class" << endl;
     delete (pcPtr);
   } 
     /* Overloading dereferencing operator */
   int &operator *() {
     cout << "I am in the overloaded function of ther class" << endl;
     return *pcPtr;
   }
};
int _tmain (int argc, _TCHAR* argv[])
{
    MySmartPtrCls pcPtr(new int());
    *pcPtr = 10;
    cout << *pcPtr;   
   getchar ();
 return 0;
}
The output of the above program is:
I am in the constructor of the smart pointer class
I am in the overloaded function of ther class
I am in the overloaded function of ther class
10

In the above program We don't need to call delete pcPtr: when the object pcPtr goes out of scope, destructor for it is automatically called and destructor does delete pcPtr.

To delete any type of the object we need to create a template class for the smart pointers.
This template class accepts any type of object as an argument. And deletes the dynamic memory automatically.

Example for the interface class:

See the below sample class that contains the overloaded operator functions
 Dereferencing (operator *)
• Indirection (operator ->)
class MyStudentCLs
{
    int iAge;
    char* pName;
    public:
        MyStudentCLs(): pName(0),iAge(0)
        {
        }
        MyStudentCLs (char* pName, int age): pName(pName), iAge(age)
        {
        }
        ~MyStudentCLs ()
        {
        }
        void DiMySmartPointerClslay ()
        {
            printf("Name of the student= %s Age of the student = %d \n", pName, iAge);
        }
        void Show ()
        {
            printf("I am at the show function");
        }
};
Example for the smart pointer interface:
class MySmartPointerCls
{
private:
    MyStudentCLs*    pData; // pointer to MyStudentCLs  class
public:
    MySmartPointerCls(MyStudentCLs* pValue) : pData(pValue)
    {
    }
    ~MySmartPointerCls()
    {
        // pointer no longer requried
        delete pData;
    }
    MyStudentCLs& operator* ()
    {
        return *pData;
    }
    MyStudentCLs* operator-> ()
    {   
        return pData;
    }
};
Calling the smart pointer in the main
int _tmain (int argc, _TCHAR* argv[])
{
 // MyStudentCLs* pMyStudentCLs  = new MyStudentCLs ("Suve", 15);
 // pMyStudentCLs->DiMySmartPointerClslay ();
//  delete pMyStudentCLs;
    MySmartPointerCls p(new MyStudentCLs("Suve", 15));
    p->Show ();
  getchar ();
  return 0;
}

For the above example, we can only delete only one class object.
If we can write the smart pointer class more generically, it will be more use for us to handle many classes. Therefore, to overcome the burden generic smart pointers are possible.

Below is the generic smart pointer with the example code:
class MyStudentCLs
{
    int iAge;
    char* pName;
    public:
        MyStudentCLs(): pName(0),iAge(0)
        {
        }
        MyStudentCLs (char* pName, int age): pName(pName), iAge(age)
        {
        }
        ~MyStudentCLs ()
        {
        }
        void DiMySmartPointerClslay ()
        {
            printf("Name of the student= %s Age of the student = %d \n", pName, iAge);
        }
        void Show ()
        {
           printf("I am at the show function");
        }
};
template < typename T > class MySmartPointerCls
{
    private:
    T*    pData; 
    public:
    MySmartPointerCls(T* pValue) : pData(pValue)
    {
    }
    ~MySmartPointerCls()
    {
        delete pData;
    }
    T& operator* ()
    {
        return *pData;
    }
    T* operator-> ()
    {
        return pData;
    }};
int _tmain (int argc, _TCHAR* argv[])
{
 // MyStudentCLs* pMyStudentCLs  = new MyStudentCLs ("Suve", 15);
 // pMyStudentCLs->DiMySmartPointerClslay ();
//  delete pMyStudentCLs;
  MySmartPointerCls<MyStudentCLs> p(new MyStudentCLs("Tarah", 25));
  p->DiMySmartPointerClslay();
  getchar ();
  return 0;
}
Now we can use our smart pointer class for any type of pointer. Fine till now

But, see the below case, which is very dangerous
 int _tmain (int argc, _TCHAR* argv[])
{
 // MyStudentCLs* pMyStudentCLs  = new MyStudentCLs ("Suve", 15);
 // pMyStudentCLs->DiMySmartPointerClslay ();
//  delete pMyStudentCLs;
    MySmartPointerCls<MyStudentCLs> ocStudent (new MyStudentCLs("Tarah", 25));
    ocStudent->DiMySmartPointerClslay ();
    {
        MySmartPointerCls <MyStudentCLs> ocStudent2 = ocStudent;
        ocStudent2->DiMySmartPointerClslay ();
        // Destructor of Q will be called here..
    }
     ocStudent->DiMySmartPointerClslay ();
    getchar ();
  return 0;
}

See here ocStudent and ocStudent2 are referring to the same MyStudentCLs class pointer.
Now when ocStudent2 goes out of scope, the destructor of ocStudent2 will be called which deletes the MyStudentCLs class pointer. Now we cannot call
ocStudent->DiMySmartPointerClslay (); 
since ocStudent will be left with a dangling pointer and this call will fail. (Note that this problem would have existed even if we were using normal pointers instead of smart pointers.) We should not delete the MyStudentCLs class pointer unless no body is using it. How do we do that? Implementing a reference counting mechanism in our smart pointer class will solve this problem.

Example program that implement the Reference counting using the smart pointers:

Reference counting calss:

class MyocRefCountingCls
{
 private:
    int iRCounter; /* ocRef iRCounter */
 public:
    void AddRef ()
    {
        /* Increment the ocRef iRCounter */
        iRCounter++;
    }
    int Release ()
    {
        /* Decrement the ocRef iRCounter and return the ocRef iRCounter. */
        return --iRCounter;
    }
};

Mystudent calss:
class MyStudentCLs
{
    int iAge;
    char* pName;
    public:
        MyStudentCLs (): pName (0),iAge (0)
        {
        }
        MyStudentCLs (char* pName, int age): pName (pName), iAge (age)
        {
        }
        ~MyStudentCLs ()
        {
        }
        void DiMySmartPointerClslay ()
        {
            printf ("Name of the student= %s Age of the student = %d \n", pName, iAge);
        }
        void Show ()
        {
            printf("I am at the show function");
        }
};

In Our We need to create the a pointer to class MyocRefCountingCls in our MySmartPointerCls class and this pointer will be shared for all instances of the smart pointer which refers to the same pointer. For this to happen, we need to have an assignment operator and copy constructor in our SP class.
template < typename T > class MySmartPointerCls
{
    private:
    T*    pData;       // pointer
    MyocRefCountingCls* ocRef; // ocRef count
public:
    MySmartPointerCls() : pData(0), ocRef(0)
    {
        // Create a new ocRef
        ocRef = new MyocRefCountingCls();
        // Increment the ocRef count
        ocRef->AddRef();
    }
    MySmartPointerCls(T* pValue) : pData(pValue), ocRef(0)
    {
        // Create a new ocRef
        ocRef = new MyocRefCountingCls();
        // Increment the ocRef count
        ocRef->AddRef();
    }
    MySmartPointerCls(const MySmartPointerCls<T>& MySmartPointerCls) : pData(MySmartPointerCls.pData), ocRef(MySmartPointerCls.ocRef)
    {
        // Copy constructor
        // Copy the data and ocRef pointer
        // and increment the ocRef count
        ocRef->AddRef();
    }
    ~MySmartPointerCls()
    {
        // Destructor
        // Decrement the ocRef count
        // if ocRef become zero delete the data
        if(ocRef->Release() == 0)
        {
            delete pData;
            delete ocRef;
        }
    }
    T& operator* ()
    {
        return *pData;
    }
    T* operator-> ()
    {
        return pData;
    }
    MySmartPointerCls<T>& operator = (const MySmartPointerCls<T>& MySmartPointerCls)
    {
        // Assignment operator
        if (this != &MySmartPointerCls) // Avoid self assignment
        {
            // Decrement the old ocRef count
            // if ocRef become zero delete the old data
            if(ocRef->Release() == 0)
            {
                delete pData;
                delete ocRef;
            }
            // Copy the data and ocRef pointer
            // and increment the ocRef count
            pData = MySmartPointerCls.pData;
            ocRef = MySmartPointerCls.ocRef;
            ocRef->AddRef();
        }
        return *this;
    }
};
int _tmain (int argc, _TCHAR* argv[])
{
 // MyStudentCLs* pMyStudentCLs  = new MyStudentCLs ("Suve", 15);
 // pMyStudentCLs->DiMySmartPointerClslay ();
//  delete pMyStudentCLs;
    MySmartPointerCls<MyStudentCLs> ocStudent (new MyStudentCLs("Tarah", 25));
    ocStudent->DiMySmartPointerClslay ();
    {
        MySmartPointerCls <MyStudentCLs> ocStudent2 = ocStudent;
        ocStudent2->DiMySmartPointerClslay ();
        // Destructor of Q will be called here..
    }
     ocStudent->DiMySmartPointerClslay ();
    getchar ();
  return 0;
}

The output of the above program is:

Name of the student= Tarah Age of the student = 25
Name of the student= Tarah Age of the student = 25
Name of the student= Tarah Age of the student = 25

Using the smart pointers, we can reduce the risk of managing the pointers, this leads to our code without memory leaks.

No comments: