Click here to Skip to main content
15,867,686 members
Articles / Programming Languages / C++
Article

A generic C++ template class to implement a smart pointer

Rate me:
Please Sign up or sign in to vote.
1.40/5 (4 votes)
5 Oct 2002CPOL5 min read 86.1K   21   7
Writing a generic C++ template class to implement a smart pointer for automatic deletion of memory

Introduction

Smart pointers are very widely used in the C++ world to avoid any pointers to be left dangling in the memory. Smart pointers are usually designed with two main goals in mind, namely - to make ones programming safer and also to make it easier. These are objects that are similar to normal pointers except that it offers great functionality while handling the memory. Normally in C++ smart pointers are available as templates, from which we can directly construct a smart pointer that will reference our class. Even in complex COM programs, smart pointers are widely used to avoid code that is error prone to memory leaks. Overloading the -> operator is useful for creating a smart pointer. This helps objects act like pointers and in addition perform some action whenever an object is accessed through them. Similarly the * operator can be overloaded to return the pointer at any given instance. STL also provides a template auto_ptr that enables smooth handling of memory. Apart from overloading the operators =, * and ->, it also provides functions to get and release the references. Similarly Microsoft ATL and COM also provides construction of smart pointers. Here are three major aspects that are supported by smart pointers that helps improving the quality of client-side COM program

  • Construction and Destruction - Construction of a smart interface pointer can be as simple as initializing the pointer to null or as complex as calling CoCreateInstance to retrieve a pointer to the requested interface. Normally, a smart pointer class's constructor is overloaded to support all of these various capabilities. The destructor of a smart interface pointer can automatically call Release.
  • Copying and Assignment - A smart pointer can overload the assignment operator (=) so that any attempt to copy the pointer results in an automatic AddRef call, relieving the programmer of the burden of reference counting.
  • Dereferencing - Last the indirection operator (*) can be overridden by the smart pointer class to perform some basic error checking, ensuring that the internal "dumb" pointer is not null or invalid in some other way.

Why write your own Smart Pointer?

Normally it is a painful job in C++ to handle memory. Unlike Java, C++ does not have the concept of garbage collection wherein it can automatically release the memory once its reference is NULL. In C++ it becomes the responsibility of the allocator to deallocate the memory, otherwise the program consumes the heap. This normally happens when some programmer uses new to allocate memory but does not follow-up with delete once this memory is used. One of the ways to tackle this is to write a generic template class that will automatically delete the allocated memory. Above all, everyone would be interested in writing their own smart pointers. This helps in understanding the concept as well as the internal working of smart pointers in a better way. Since there is a way where you can commit your own smart pointer, then why not give a try and see how it practically works. Before implementing a template of a smart pointer, let us consider the example below that will explain how normally memory leak occurs.

//A simple class that will allocate memory to a string and 
//display it using a function
#include<iostream.h>
#include<string.h>
class MyClass
{
public:
    //ctor
    MyClass()
    {
        str=new char[255];
    }
    //dtor
    ~MyClass()
    {
        delete[] str;
    }
    //Function to display the string
    void display()
    {
        strcpy(str,"This is a test string");
        cout<<str<<endl;
    }
private:
    char* str;
};

Now if you have a main program that instantiates this class and allocates the memory and makes a call to the display function. Consider the code below

//Main function
void main()
{
    MyClass* pMyClass=new MyClass();
    pMyClass->display();
}

In the above program you can see that delete is not called after allocating memory using new. This certainly leads to memory leaks. Since this is small piece of program it is easy to track and delete the memory. But in a real world situation where tracking becomes little difficult, it is always advisable to write a template that will help you deallocate the memory automatically once the usage is over. Below is a template class implementation that will help you deallocate the memory

#include<iostream.h>
#include<string.h>

//The template class definition for smart pointer
template<class T>
class AUTO_PTR 
{
public:
    typedef T element_type;
    //ctor
    explicit AUTO_PTR(T *pVal = 0) throw()
    {
        if(pVal)
            m_AutoPtr = pVal;
        else
            m_AutoPtr = NULL;
    }
    //copy ctor
    AUTO_PTR(const AUTO_PTR<T>& ptrCopy) throw()
    {
        if(ptrCopy)
            m_AutoPtr = ptrCopy;
        else
            m_AutoPtr = 0;
    }
    //overloading = operator
    AUTO_PTR<T>& operator=(AUTO_PTR<T>& ptrCopy) throw()
    {
        if(ptrCopy)
            m_AutoPtr = &ptrCopy;
        else
            m_AutoPtr = 0;
        return m_AutoPtr;
    }
    //dtor
    ~AUTO_PTR()
    {
        if(m_AutoPtr)
        {
            delete m_AutoPtr;
        }
    }
    //overloading * operator
    T& operator*() const throw()
    {
        return *m_AutoPtr;
    }
    //overloading -> operator
    T *operator->() const throw()
    {
        return m_AutoPtr;
    }
    //function to get the pointer to the class
    T *get() const throw()
    {
        return m_AutoPtr;
    }
private:
    T *m_AutoPtr;
};

class MyClass
{
public:
    //ctor
    MyClass()
    {
        str=new char[255];
        cout<<"Memory allocated"<<endl;
    }
    //dtor
    ~MyClass()
    {
        delete[] str;
        cout<<"Memory deallocated"<<endl;
    }
    //Function to display the string
    void display()
    {
        strcpy(str,"This is a test string");
        cout<<str<<endl;
    }
private:
    char* str;
};

Now the main() function will have to be modified such that it uses the AUTO_PTR template class to allocate and deallocate the memory. This is how the main() function looks after modification

void main()
{
    //Allocate memory to the class MyClass using the template
    AUTO_PTR<MyClass> b=AUTO_PTR<MyClass>(new MyClass);
    //Get the pointer and call the display function
    b.get()->display();
}

How does the above program work?

Look into the main function. There one pointer of the type MyClass being created with the help of AUTO_PTR template. At first it enters the MyClass constructor and allocates the memory as required. It then enters the AUTO_PTR constructor and creates a smart pointer of the type MyClass. This pointer is used till the life time of the reference MyClass. The function get is called to seek a reference to the class MyClass and then the function display is called to display the string allocated by the class. Once the reference is used, it automatically de-references the allocated memory. This happens in reverse order as you know that the destructors are called in reverse order. Though the program never bothers to make a call to delete, Since the working of smart pointers are so that it dereferences the memory, it first enters the destructor of the AUTO_PTR to delete the pointer m_AutoPtr and then enters the destructor of the class MyClass to release the memory allocated to the string variable. Hence at the end of the program the memory is released.

The above example is very simple. This is just to show how small mistakes leads to memory leaks. This can be elaborated to use the template in a complex scenario. Treat the class MyClass as an example to show the usage of the template. You can write much more complex classes and use the template to allocate and deallocate the memory. Have fun experimenting with more complex classes. Any doubts, contact me at bkumarp@yahoo.com

License

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


Written By
Web Developer
India India
I have completed my post-graduation in computer applications and working as software engineer in a software firm.

Comments and Discussions

 
QuestionParameters? Pin
Chen Jiadong10-Oct-02 21:52
Chen Jiadong10-Oct-02 21:52 
AnswerRe: Parameters? Pin
Binoy15-Dec-02 23:13
Binoy15-Dec-02 23:13 
GeneralUse shared_ptr from Boost.org! Pin
jhwurmbach6-Oct-02 22:03
jhwurmbach6-Oct-02 22:03 
GeneralRe: Use shared_ptr from Boost.org! Pin
Binoy8-Oct-02 2:05
Binoy8-Oct-02 2:05 
GeneralRe: Use shared_ptr from Boost.org! Pin
jhwurmbach8-Oct-02 2:11
jhwurmbach8-Oct-02 2:11 
GeneralThe copy constructors won't work properly Pin
Dave Bryant6-Oct-02 9:21
Dave Bryant6-Oct-02 9:21 
In the copy constructors, you are merely copying the internal pointer so that it is now referenced in two AUTO_PTR objects, rather than just one. Since this is not reference counting, they will both try and free the memory in their destructors, resulting in an access violation. To avoid this, you either need to prohibit copying (not particularly useful), or when an AUTO_PTR is copied, the source pointer must be set to null.

Dave
GeneralRe: The copy constructors won't work properly Pin
Binoy8-Oct-02 2:08
Binoy8-Oct-02 2:08 

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.