Click here to Skip to main content
Click here to Skip to main content

Implementing a simple smart pointer in C++

By , 27 Aug 2006
 

Introduction

What are smart pointers? The answer is fairly simple; a smart pointer is a pointer which is smart. What does that mean? Actually, smart pointers are objects which behave like pointers but do more than a pointer. These objects are flexible as pointers and have the advantage of being an object (like constructor and destructors called automatically). A smart pointer is designed to handle the problems caused by using normal pointers (hence called smart).

Problems with pointers

What are the common problems we face in C++ programs while using pointers? The answer is memory management. Have a look at the following code:

char* pName  = new char[1024];
…
SetName(pName);
…
…
if(null != pName)
{
       delete[] pName; 
}

How many times have we found a bug which was caused because we forgot to delete pName. It would be great if someone could take care of releasing the memory when the pointer is not useful (we are not talking about the garbage collector here). What if the pointer itself takes care of that? Yes, that’s exactly what smart pointers are intended to do. Let us write a smart pointer and see how we can handle a pointer better.

We shall start with a realistic example. Let’s say we have a class called Person which is defined as below.

class Person
{
    int age;
    char* pName;

    public:
        Person(): pName(0),age(0)
        {
        }
        Person(char* pName, int age): pName(pName), age(age)
        {
        }
        ~Person()
        {
        }

        void Display()
        {
            printf("Name = %s Age = %d \n", pName, age);
        }
        void Shout()
        {
            printf("Ooooooooooooooooo",);
        } 
};

Now we shall write the client code to use Person.

void main()
{
    Person* pPerson  = new Person("Scott", 25);
    pPerson->Display();
    delete pPerson;
}

Now look at this code, every time I create a pointer, I need to take care of deleting it. This is exactly what I want to avoid. I need some automatic mechanism which deletes the pointer. One thing which strikes to me is a destructor. But pointers do not have destructors, so what? Our smart pointer can have one. So we will create a class called SP which can hold a pointer to the Person class and will delete the pointer when its destructor is called. Hence my client code will change to something like this:

void main()
{
    SP p(new Person("Scott", 25));
    p->Display();
    // Dont need to delete Person pointer..
}

Note the following things:

  • We have created an object of class SP which holds our Person class pointer. Since the destructor of the SP class will be called when this object goes out of scope, it will delete the Person class pointer (as its main responsibility); hence we don’t have the pain of deleting the pointer.
  • One more thing of major importance is that we should be able to call the Display method using the SP class object the way we used to call using the Person class pointer, i.e., the class should behave exactly like a pointer.

Interface for a smart pointer

Since the smart pointer should behave like a pointer, it should support the same interface as pointers do; i.e., they should support the following operations.

  • Dereferencing (operator *)
  • Indirection (operator ->)

Let us write the SP class now.

class SP
{
private:
    Person*    pData; // pointer to person class
public:
    SP(Person* pValue) : pData(pValue)
    {
    }
    ~SP()
    {
        // pointer no longer requried
        delete pData;
    }

    Person& operator* ()
    {
        return *pData;
    }

    Person* operator-> ()
    {    
        return pData;
    }
};

This class is our smart pointer class. The main responsibility of this class is to hold a pointer to the Person class and then delete it when its destructor is called. It should also support the interface of the pointer.

Generic smart pointer class

One problem which we see here is that we can use this smart pointer class for a pointer of the Person class only. This means that we have to create a smart pointer class for each type, and that’s not easy. We can solve this problem by making use of templates and making this smart pointer class generic. So let us change the code like this:

template < typename T > class SP
{
    private:
    T*    pData; // Generic pointer to be stored
    public:
    SP(T* pValue) : pData(pValue)
    {
    }
    ~SP()
    {
        delete pData;
    }

    T& operator* ()
    {
        return *pData;
    }

    T* operator-> ()
    {
        return pData;
    }
};

void main()
{
    SP<PERSON> p(new Person("Scott", 25));
    p->Display();
    // Dont need to delete Person pointer..
}

Now we can use our smart pointer class for any type of pointer. So is our smart pointer really smart? Check the following code segment.

void main()
{
    SP<PERSON> p(new Person("Scott", 25));
    p->Display();
    {
        SP<PERSON> q = p;
        q->Display();
        // Destructor of Q will be called here..
    }
    p->Display();
}

Look what happens here. p and q are referring to the same Person class pointer. Now when q goes out of scope, the destructor of q will be called which deletes the Person class pointer. Now we cannot call p->Display(); since p 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 Person 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.

Reference counting

What we are going to do is we will have a reference counting class RC. This class will maintain an integer value which represents the reference count. We will have methods to increment and decrement the reference count.

class RC
{
    private:
    int count; // Reference count

    public:
    void AddRef()
    {
        // Increment the reference count
        count++;
    }

    int Release()
    {
        // Decrement the reference count and
        // return the reference count.
        return --count;
    }
};

Now that we have a reference counting class, we will introduce this to our smart pointer class. We will maintain a pointer to class RC in our SP 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 SP
{
private:
    T*    pData;       // pointer
    RC* reference; // Reference count

public:
    SP() : pData(0), reference(0) 
    {
        // Create a new reference 
        reference = new RC();
        // Increment the reference count
        reference->AddRef();
    }

    SP(T* pValue) : pData(pValue), reference(0)
    {
        // Create a new reference 
        reference = new RC();
        // Increment the reference count
        reference->AddRef();
    }

    SP(const SP<T>& sp) : pData(sp.pData), reference(sp.reference)
    {
        // Copy constructor
        // Copy the data and reference pointer
        // and increment the reference count
        reference->AddRef();
    }

    ~SP()
    {
        // Destructor
        // Decrement the reference count
        // if reference become zero delete the data
        if(reference->Release() == 0)
        {
            delete pData;
            delete reference;
        }
    }

    T& operator* ()
    {
        return *pData;
    }

    T* operator-> ()
    {
        return pData;
    }
    
    SP<T>& operator = (const SP<T>& sp)
    {
        // Assignment operator
        if (this != &sp) // Avoid self assignment
        {
            // Decrement the old reference count
            // if reference become zero delete the old data
            if(reference->Release() == 0)
            {
                delete pData;
                delete reference;
            }

            // Copy the data and reference pointer
            // and increment the reference count
            pData = sp.pData;
            reference = sp.reference;
            reference->AddRef();
        }
        return *this;
    }
};

Let us have a look at the client code.

void main()
{
    SP<PERSON> p(new Person("Scott", 25));
    p->Display();
    {
        SP<PERSON> q = p;
        q->Display();
        // Destructor of q will be called here..

        SP<PERSON> r;
        r = p;
        r->Display();
        // Destructor of r will be called here..
    }
    p->Display();
    // Destructor of p will be called here 
    // and person pointer will be deleted
}

When we create a smart pointer p of type Person, the constructor of SP will be called, the data will be stored, and a new RC pointer will be created. The AddRef method of RC is called to increment the reference count to 1. Now SP q = p; will create a new smart pointer q using the copy constructor. Here the data will be copied and the reference will again be incremented to 2. Now r = p; will call the assignment operator to assign the value of p to q. Here also we copy the data and increment the reference count, thus making the count 3. When r and q go out of scope, the destructors of the respective objects will be called. Here the reference count will be decremented, but data will not be deleted unless the reference count becomes zero. This happens only when the destructor of p is called. Hence our data will be deleted only when no body is referring to it.

Applications

Memory leaks: Using smart pointers reduces the work of managing pointers for memory leaks. Now you could create a pointer and forget about deleting it, the smart pointer will do that for you. This is the simplest garbage collector we could think of.

Exceptions: Smart pointers are very useful where exceptions are used. For example, look at the following code:

void MakeNoise()
{
    Person* p = new Person("Scott", 25);
    p->Shout();
    delete p;
}

We are using a normal pointer here and deleting it after using, so every thing looks okay here. But what if our Shout function throws some exception? delete p; will never be called. So we have a memory leak. Let us handle that.

void MakeNoise()
{
    Person* p = new Person("Scott", 25);
    try
    {
        p->Shout();
    }
    catch(...)
    {
        delete p;
        throw;
    }
    delete p;
}

Don't you think this is an overhead of catching an exception and re-throwing it? This code becomes cumbersome if you have many pointers created. How will a smart pointer help here? Let's have a look at the same code if a smart pointer is used.

void MakeNoise()
{
    SP<Person> p(new Person("Scott", 25));
    p->Shout();
}

We are making use of a smart pointer here; yes, we don’t need to catch the exception here. If the Shout method throws an exception, stack unwinding will happen for the function and during this, the destructor of all local objects will be called, hence the destructor of p will be called which will release the memory, hence we are safe. So this makes it very useful to use smart pointers here.

Conclusion

Smart pointers are useful for writing safe and efficient code in C++. Make use of smart pointers and take the advantage of garbage collection. Take a look at Scott Meyers' auto_ptr implementation in STL.

License

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

About the Author

Madhu Raykar
Web Developer
India India
Member
I have been working in software industry for the past 5 years now. Enjoy working on complex and new technologies. Worked on Microsoft technologies Like VC++, COM, XML, SQL. Currently working on .Net, C#.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
SuggestionRC::count should be initialized to zeromemberRaghavendra Ramanujam22 Feb '13 - 3:13 
RC::count should be initialized to zero
QuestionQuestionmemberchandrak027 Jan '13 - 2:27 
In destructor ~sp()
 
referance->release() method is not called.
GeneralMy vote of 5membersaikrishna_n20 Dec '12 - 2:47 
excellent..well explained
GeneralRe: My vote of 5membersaikrishna_n20 Dec '12 - 23:20 
can any bode explain, what is the use of the following code in the template class
 
// Decrement the old reference count
            // if reference become zero delete the old data
            if(reference->Release() == 0)
            {
                delete pData;
                delete reference;
            }

GeneralMy vote of 5memberEkviy3 Oct '12 - 2:22 
thx a lot. Simple explanation of difficult topic
GeneralMy vote of 5membernaresh_pallam7 Jan '11 - 0:07 
Nice explanation with appropriate examples.
GeneralMy vote of 4memberKarukuri3 Oct '10 - 9:54 
Good step by step intro to Smart pointers.
Generalauto_ptrmembermynameisdave4 Sep '08 - 1:00 
Since it is clear from the reference at the end of the article that an implementation already exists in the STL, which is part of every C++ implementation( if it is standard) why not use auto_ptr as the example of the discussion.
I don't think a novice to smart pointers is well served by yet another implementation with only a passing reference to STL at the end of the article.
Otherwise a very good discussion of why and how smart pointers should be used.
AnswerRe: auto_ptrmemberFroggr9 Jun '12 - 11:19 
auto_ptr doesn't hold a reference count which is important if you wish to share the data pointed to. With auto_ptr the last assigned auto_ptr will always own and destroy the data. This is very useful in scenarios like returning a pointer from a function. If the function throws an exception and doesn't return then the pointer data will be deleted automatically.
 
However if you wish to share data you can't use auto_ptr since if the last assigned auto_ptr dies it deletes the data even if it is used somewhere else. This is when you need reference counting.
 
A good example is STL containers. You can't use auto_ptr in a vector for example. The smart pointer in this article however works just fine with a vector.
QuestionMissing RC constructor?memberl00p1n62 Aug '07 - 22:10 
I think that RC constructor should put "count" to "0"
AnswerMissplaced comments?memberl00p1n62 Aug '07 - 22:28 
{
SP q = p;
q->Display();
// Destructor of q will be called here..
 
SP r;
r = p;
r->Display();
// Destructor of r will be called here..
}
 
Are u sure? I think it will be like this:
 
{
SP q = p;
q->Display();
 
SP r;
r = p;
r->Display();
 
// Destructor of q will be called here..
// Destructor of r will be called here..
}
GeneralBugmemberAnonymuos31 Aug '06 - 23:53 
SP<T>& operator = (const SP<T>& sp)
{
  // Assignment operator
  if (this != &sp) // Avoid self assignment
  {
      // Decrement the old reference count
      // if reference become zero delete the old data
      if(<big>reference &&</big> reference->Release() == 0) //!!
      {
          delete pData;
          delete reference;
      }
// ...
}

GeneralDynamic CastmemberEd Deighton29 Aug '06 - 5:50 
Another handy thing is to support dynamic casts i.e.
 
CSmartPtr<CBase> pBasePtr( new CDerived );
 
if(CSmartPtr<CDerived> pDerivedPtr =
      pBasePtr->dynamic_cast<CDerived>())
{
   pDerivedPtr->xyz()
   ...
}
 
where dynamic_cast is a template member function that internally actually uses the c++ dynamic_cast and appropriately returns a smart pointer that is either set or NULL and so on.
 

GeneralRe: not so smart ...memberMadhu Raykar28 Aug '06 - 17:59 
Yeah, before using smart pointers i think we should know how smart pointers work (that why i wrote this article Wink | ;) ). And while using them we should be cautious enough to provide proper pointers to them. Big Grin | :-D
 
BTW the main responisablity of smart pointer is to maintain the life time of the pointer and not to validate the correctness of the pointer. Hope this answers your concerns of not using smart pointers. Big Grin | :-D

GeneralPointer comparisons and other functionalitymemberAArnott28 Aug '06 - 4:32 
Again, great article.
 
Your SP class would benefit from some very useful operators (like == and !=).   There may also be room for an explicit cast operator and/or a GetPointer() and ReleasePointer() method so that smart pointers can be passed to functions that only accept dumb pointers.
 
Here is the == and != operator code:
     bool operator == (const SP<T>& sp)
     {
          return pData == sp.pData;
     }
    
     bool operator != (const SP<T>& sp)
     {
          return pData != sp.pData;
     }
 

BTW, your void Main() method doesn't use the SP class where you have it using templates.   The correct syntax would be:
            SP<Person> p(new Person("Scott", 25));
GeneralRe: Pointer comparisons and other functionalitymemberMadhu Raykar28 Aug '06 - 17:49 
Hi,
Thanks for the suggestion Smile | :)
I will add this code to the article, and about the syntax error in Main method is because of the HTML tag, it has taken as tag, i shall modify the article.
 
Thanks again
bye

GeneralCoolmemberwaldermort27 Aug '06 - 7:26 
Hey that was a great article. I have just one question though, can we implement something like this with GDI objects? My one downfall is never cleaning up correctly the first time round after a series of bitblts and selectObject's.
GeneralRe: CoolmemberMadhu Raykar27 Aug '06 - 19:01 
Yes, you can use this for GDI objects also.Smile | :)
GeneralCircular dependenciesmemberAArnott27 Aug '06 - 5:22 
Great article. Thanks for giving us a great template for a smart pointer.
 
Just one addition to your article is in order as a warning: that these smart pointers will not properly dispose of objects that are part of a circular dependency. You could end up with A pointing to B and vice versa, and even when all pointers to A and B (outside these two) are gone, they will never be deleted.
GeneralRe: Circular dependenciesmemberMadhu Raykar28 Aug '06 - 0:09 
I think this is taken care.., Confused | :confused: can you explain with more realistic example.
Thanks in advance..
AnswerRe: Circular dependencies [modified]memberAArnott28 Aug '06 - 4:06 
Suppose you modified your Person class slightly to include a pointer to a friend person:
class Person
{
/// ...
SP<Person> friendPerson;
}
 
Then by creating two people and making them friends, you end up with a circular reference that keeps that reference counter from ever going back to zero.   As a result, the memory is never freed.
 
void circularReferenceExample()
{
     SP<Person> p(new Person("Scott", 25));
     SP<Person> f(new Person("Bill", 23));
     // These two people are friends. They point to each other,
     // generating this circular reference.
     p->SetFriend(f);
     f->SetFriend(p);
     p->Display(true);
     f->Display(true);
     // Destructors will never be called and memory only freed
     // when the program exits and OS reclaims all memory.
}
 

-- modified at 10:11 Monday 28th August, 2006
AnswerRe: Circular dependenciesmemberkazsterator2 Nov '06 - 5:51 
Don't waste your breath. It's pretty clear that the individual who wrote this article is just regurgitating information he got from somewhere else without fully comprehending it himself.
 
To deal with circular dependencies, you'll have to have either an internal or a global tracker. The way I do it is by checking out how many children point back to the parent recursively and compare that against the reference count value. That way you'll know if something else has a reference to what you're trying to delete, if not you can recursively go thru all the children and break up links pointing back to the object you're trying to delete, then delete everything without worrying.
 
I'm sure there are other ways to do it aswell.
 
-- Kaz
 
AArnott wrote:
ForumImplementing a simple smart pointer in c++
Subject:Re: Circular dependencies
Sender:AArnott
Date:10:06 28 Aug '06
 
Suppose you modified your Person class slightly to include a pointer to a friend person:
class Person
{
/// ...
SP friendPerson;
}
 
Then by creating two people and making them friends, you end up with a circular reference that keeps that reference counter from ever going back to zero. As a result, the memory is never freed.
 
void circularReferenceExample()
{
SP p(new Person("Scott", 25));
SP f(new Person("Bill", 23));
// These two people are friends. They point to each other,
// generating this circular reference.
p->SetFriend(f);
f->SetFriend(p);
p->Display(true);
f->Display(true);
// Destructors will never be called and memory only freed
// when the program exits and OS reclaims all memory.
}
 

-- modified at 10:11 Monday 28th August, 2006

 

GeneralRe: Circular dependenciesmemberCrovazTheDestroyer2 Nov '06 - 7:36 
Uh Pay no attention to Kaz's Ramblings I have valid sources that prove he has escaped from a mental institue, and has no idea what he is talkign about.
GeneralRe: Circular dependenciesmemberWim Decelle20 Jul '11 - 10:12 
This is actually correct.
 
This is a known issue for any scheme using reference counting. It's one of the main problems with f.e. the microsoft COM design and it's one of the main reasons why .NET garbage collection is graph-based rather than using merely reference counts.
 
When you refer object1 from thread0, refer object2 from object1 and object1 from object2.
Then object1 has reference count 2 (referred from object2 and from thread0);
and object2 had reference count 1 (referred from object1 only).
 
When your reference from thread0 to object1 goes out of scope, the reference count is decreased for object1... but goes to 1 and not to 0 as object1 had 2 references and the one from object2 still exists. As a result it is not cleaned up, but the object1 <-> object2 pair remains in existence, with both a reference count of 1.
 
Going back to the COM design which also has this problem, it can occur in very tricky situations: f.e. a custom .NET wrapper for a COM object might implement a COM event interface but also store the COM reference as a private member: as a result the .NET wrapper refers the COM object; but the COM object also refers the .NET wrapper since it must have a reference to it in one of it's event connectionpoints to be able to call the callback interface(s). As a result the .NET wrapper object is never collected by the garbage collecter since there is always the COM reference to it and the COM object is never released since there is always the .NET wrapper object reference to it.
 
In COM this is taken care of by introducing 'weak references' to the objects... which mean you will simply not to do the reference counting for one of the reference directions when you expect problems and then inform that direction to release it pointer when it's released in the other direction.
 
Another solution is to maintain object graphs, but then each dereference (where you would normally decrement the reference count) would require walking or partitioning the graph which becomes a serious overhead for larger applications... as a result an actual garbage collection thread or threadpool becomes the viable solution to defer the graph-walking/partitioning to a point in time where more memory can be released at once.
GeneralRe: Circular dependenciesmemberErudite_Eric20 Dec '12 - 23:56 
Or just do what we do in the kernel, allocate and free memory directly and never get it wrong. Smile | :)
==============================
 
Nothing to say.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 27 Aug 2006
Article Copyright 2006 by Madhu Raykar
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid