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.
#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
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;
}
AUTO_PTR<T>& operator=(AUTO_PTR<T>& ptrCopy) throw()
{
if(ptrCopy)
m_AutoPtr = &ptrCopy;
else
m_AutoPtr = 0;
return m_AutoPtr;
}
~AUTO_PTR()
{
if(m_AutoPtr)
{
delete m_AutoPtr;
}
}
T& operator*() const throw()
{
return *m_AutoPtr;
}
T *operator->() const throw()
{
return m_AutoPtr;
}
T *get() const throw()
{
return m_AutoPtr;
}
private:
T *m_AutoPtr;
};
class MyClass
{
public:
MyClass()
{
str=new char[255];
cout<<"Memory allocated"<<endl;
}
~MyClass()
{
delete[] str;
cout<<"Memory deallocated"<<endl;
}
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()
{
AUTO_PTR<MyClass> b=AUTO_PTR<MyClass>(new MyClass);
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