August 15, 2018
Hot Topics:

Coding Tidbits and Style That Saved My Butt

  • March 23, 2005
  • By Mike McShaffry
  • Send Email »
  • More Articles »

Smart Pointers and Naked Pointers

Clearly, all smart pointers wear clothing.

If you declare a pointer to another object, you've just used a naked pointer. Pointers are used to refer to another object, but they don't convey enough information. Anything declared on the heap must be referenced by at least one other object or it can never be freed, causing a memory leak. It is common for an object on the heap to be referred multiple times by other objects in the code. A good example of this is a game object like a clock. A pointer to the clock will exist in the game object list, the physics system, the graphics system, and even the sound system.

If you use naked pointers you must remember which objects implicitly own other objects. An object that owns other objects controls their existence. Imagine a ship object that owns everything on the ship. When the ship sinks, everything else is destroyed along with it. If you use naked pointers to create these relationships you have to remember who owns who. This can be a confusing or even impossible task. You'll find that using naked pointers will quickly paint you into a corner.

Smart pointers, on the other hand, hold extra information along with the address of the distant object. This information can count references, record permanent or temporary ownership, or perform other useful tasks. In a sense an object controlled by a smart pointer "knows" about every reference to itself. The horrible nest of naked pointers evaporates, leaving a simple and foolproof mechanism for handling your dynamic objects.

Reference Counting

Reference counting stores an integer value that counts how many other objects are currently referring to the object in question. Reference counting is a common mechanism in memory management. DirectX objects implement the COM based IUnknown interface, which uses reference counting Two methods that are central to this task are AddRef()and Release(). The following code shows how this works:

MySound *sound = new MySound;
sound->AddRef();               //reference count is now 1

After you construct a reference counted object, you call the AddRef() method to increase the integer reference counter by one. When the pointer variable goes out of scope, by normal scoping rules or by the destruction of the container class, you must call Release(). Release() will decrement the reference counter and destroy the object if the counter drops to zero. A shared object can have multiple references safely without fear of the object being destroyed, leaving bad pointers all over the place.

Best Practice

Good reference counting mechanisms automatically delete the object when the reference count becomes zero. If the API leaves the explicit destruction of the object to you, it's easy to create memory leaks—all you have to do is forget to call Release(). You can also cause problems if you forget to call AddRef() when you create the object. It's likely that the object will get destroyed unexpectedly, not having enough reference counts.

Anytime you assign a pointer variable to the address of the reference counted object you'll do the same thing. This includes any calls inside a local loop:

for (int i=0; i<m_howMany; ++i)
   MySound *s = GoGrabASoundPointer(i);


   if (s->IsPlaying())


This kind of code exists all over the place in every game I've ever worked on. The call to DangerousFunction() goes deep and performs some game logic that might attempt to destroy the instance of the MySound object. Don't forget that in a release build that the deallocated memory retains the same values until it is reused. It's quite possible that the loop will work just fine even though the MySound pointer is pointing to unallocated memory. What's more likely to occur is a terrible corruption of memory.

Reference counting keeps the sound object around until Release() is called at the bottom of the loop. If there was only one reference to the sound before the loop started, the call to AddRef() will add one to the sound's reference count, making two references. DangerousFunction() does something that destroys the sound, but through a call to Release(). As far as DangrousFunction() is concerned, the sound is gone forever. It still exists because one more reference to it, through MySound *s, kept the reference count from dropping to zero inside the loop. The final call to Release() causes the destruction of the sound.

Boost C++'s shared_ptr

If you think calling AddRef() and Release() all over the place might be a serious pain in the ass, you're right. It's really easy to forget an AddRef() or a Release() call, and your memory leak will be almost impossible to find. It turns out that there are plenty of C++ templates out there that implement reference counting in a way that handles the counter manipulation automatically. One of the best examples is the shared_ptr template class in the Boost C++ library, found at www.boost.org/.

Here's an example on how to use this template:

#include <boost \config.hpp>
#include <boost \shared_ptr.hpp>

using boost::shared_ptr;

class IPrintable
   virtual void VPrint()=0;

class CPrintable : public IPrintable
   char *m_Name;
   CPrintable(char *name)  { m_Name = name;printf("create %s \n",m_Name); }
   virtual ~CPrintable()   { printf("delete %s \n",m_Name); }
   void VPrint()           { printf("print %s \n",m_Name); }

shared_ptr<CPrintable> CreateAnObject(char *name)
   return shared_ptr<CPrintable>(new CPrintable(name));

void ProcessObject(shared_ptr<CPrintable> o)
   printf("(print from a function) ");
void TestSharedPointers(void)
   shared_ptr<CPrintable> ptr1(new CPrintable("1"));  //create object 1
   shared_ptr>CPrintable> ptr2(new CPrintable("2"));  //create object 2

   ptr1 = ptr2;                                       //destroy object 1
   ptr2 = CreateAnObject("3")                         //used as a return value
   ProcessObject(ptr1);                               //call a function
   CPrintable o1("bad");
   //ptr1 =&o1    //Syntax error!It's on the stack....
   CPrintable *o2 = new CPrintable("bad2");
   //ptr1 = o2;    //Syntax error!Use the next line to do this...

   ptr1 = shared_ptr<CPrintable>(o2);

   //You can even use shared_ptr on ints!

   shared_ptr<int>a(new int);
   shared_ptr<int>b(new int);

   *a = 5;
   *b = 6;

   const int *q = a.get();    //use this for reading in multithreaded code
   //this is especially cool - you can also use it in lists.
   std::list< shared_ptr<int> > intList;
   std::list< shared_ptr<IPrintable> >printableList;
   for (int i=0; i<100; ++i)
      intList.push_back(shared_ptr<int>(new int(rand())));
      printableList.push_back(shared_ptr<IPrintable>(new CPrintable("list")));

   //No leaks!!!! Isn't that cool...

The template classes use overloaded assignment operators and copy operators to keep track of how many references point to the allocated data. As long as the shared_ptr object is in scope and you behave yourself by avoiding the bad usage cases you won't leak memory and you won't have to worry about objects getting destroyed while you are still referencing them from somewhere else.

This smart pointer even works in multithreaded environments as long as you follow a few rules. First, don't write directly to the data. You can access the data through const operations such as the .get() method. As you can also see, the template works fine if it is inside an STL container such as std::list.

Best Practice

Don't ignore multithreaded access to shared memory blocks. You might think that the chances of two threads accessing the shared data are exceedingly low, and convince yourself that you don't need to go to the trouble of adding multithreaded protection. You'd be wrong, every time.

Page 3 of 4

Comment and Contribute


(Maximum characters: 1200). You have characters left.



Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

By submitting your information, you agree that developer.com may send you developer offers via email, phone and text message, as well as email offers about other products and services that developer believes may be of interest to you. developer will process your information in accordance with the Quinstreet Privacy Policy.


Thanks for your registration, follow us on our social networks to keep up-to-date