Smart Pointers

The most powerful, and probably the most annoying (for beginners), feature of C/C++ has to offer is pointers. Improperly handling pointers can lead to memory leak and ultimately crash your program for running out of memory. A number of new technologies or languages have been developed to solve this problem. Newer languages manages memory management automatically like Java and langauges managed by the .Net Framework Common Language Runtime. These languages definitely made programmers’ life easier. But unfortunately, none of these, beside assembly, offer as much power and performance that C/C++ offers.

(NOTE: The implementation of the SmartPointer classes uses OOP paradigm. So we’ll be focusing on C++ from here on…)
If you want to stick using C++ but want to minimize pointer management, there is one solution. It’s called Smart Pointers.

The concept of Smart Pointer that I will present to you below basically encapsulates the data into another layer. That layer carries a counter to keep track of how many pointers point to the data. When the counter reaches 0, it means that no pointer points to the data and it is safe to delete now. All of these operations are performed automatically by defining important operations.

 

Let’s start with the layer class that encapsulates the data.

  1. class SmartData
  2. {
  3. public:
  4.  
  5.     SmartData();
  6.     SmartData( const T* pData );
  7.  
  8.     /*virtual*/ void AddRef( void );
  9.     /*virtual*/ void Release( void );
  10.  
  11.     /*virtual*/ T * GetData( void );
  12.     /*virtual*/ const T * GetData( void ) const;
  13.     /*virtual*/ void SetData( T *pData );
  14.  
  15. protected:
  16.  
  17.     friend SmartPointer;
  18.  
  19.     T        *m_pData;
  20.     SIZE_T    m_refCount;
  21. };

SmartData is the class that’s responsible for storing the data and keeping track of the reference counter. ( NOTE this class is inside another templated class. T is a given type from the template argument list.)

 

Now that we have the internal structure down here’s the entire class of SmartPointer

  1. template < typename T >
  2. class SmartPointer
  3. {
  4. public:
  5.  
  6.     SmartPointer( void );
  7.     SmartPointer( const SmartPointer< T > &smartPointer );
  8.     SmartPointer( const T *pData );
  9.     virtual ~SmartPointer();
  10.  
  11.     const T & operator * ( void ) const;
  12.     T & operator * ( void );
  13.     T * operator -> ( void ) const;
  14.  
  15.     BOOL operator == ( const SmartPointer< T > &rhs ) const;
  16.  
  17.     SmartPointer & operator = ( const SmartPointer< T > &rhs );
  18.     virtual SmartPointer & operator = ( const T *pData );
  19.  
  20.     void Reset( const T *pData );
  21.     void Reset( void );
  22.  
  23.     const SIZE_T GetRefCount( void );
  24.  
  25.     BOOL IsNull( void );
  26.  
  27. protected:
  28.  
  29.     void Release();
  30.     void AddRef();
  31.  
  32.     class SmartData /*: public ISmartData*/
  33.     {
  34.     public:
  35.  
  36.         SmartData();
  37.         SmartData( const T* pData );
  38.  
  39.         /*virtual*/ void AddRef( void );
  40.         /*virtual*/ void Release( void );
  41.  
  42.         /*virtual*/ T * GetData( void );
  43.         /*virtual*/ const T * GetData( void ) const;
  44.         /*virtual*/ void SetData( T *pData );
  45.  
  46.     protected:
  47.  
  48.         friend SmartPointer;
  49.  
  50.         T        *m_pData;
  51.         SIZE_T    m_refCount;
  52.     };
  53.  
  54.     SmartData *m_pSmartObj;
  55. };
  56.  
  57. // SmartPointer for handling arrays
  58. template < typename T >
  59. class SmartPointerArr : public SmartPointer< T >
  60. {
  61. public:
  62.  
  63.     SmartPointerArr( void );
  64.     SmartPointerArr( SmartPointerArr< T > &smartPointerArr );
  65.     explicit SmartPointerArr( T *pData );
  66.     virtual ~SmartPointerArr();
  67.  
  68.     virtual SmartPointerArr & operator = ( const T *pData );
  69.     SmartPointerArr & operator = ( const SmartPointerArr< T > &rhs );
  70.  
  71. private:
  72.  
  73.     class SmartDataArr : public SmartData
  74.     {
  75.     public:
  76.         SmartDataArr( void );
  77.         SmartDataArr( const T* pData );
  78.  
  79.         virtual void Release( void );
  80.     };
  81. };

 

From all of the defined methods, the most important method that makes all the magic work are the two assignment operators, constructor and destructor. Here are the definition of the assignment operators

 

  1. template < typename T >
  2. SmartPointer< T > & SmartPointer< T >::operator = ( const SmartPointer< T > &rhs )
  3. {
  4.     if ( this == &rhs ) return *this;
  5.  
  6.     Release();
  7.     m_pSmartObj = rhs.m_pSmartObj;
  8.     AddRef();
  9.     return *this;
  10. }
  11.  
  12. template < typename T >
  13. SmartPointer< T > & SmartPointer< T >::operator = ( const T *pData )
  14. {
  15.     if ( m_pSmartObj && m_pSmartObj->GetData() == pData ) return *this;
  16.  
  17.     Release();
  18.     m_pSmartObj = new SmartData( pData ); // ref autmatically set to 1
  19.     return *this;
  20. }

 

That should be it! I hope the code is fairly easy to understand and straightforward. Here’s a quick sample code showing you how to use it.

  1. #include "SmartPointer.h"
  2.  
  3.  
  4. void TestFunc( SmartPointer<int> pVal )
  5. {
  6.     // start of function pVal is incremented by 1
  7.     // end of function decrement pVal by 1
  8. }
  9.  
  10. int main( void )
  11. {
  12.     SmartPointer<int> p1(new int(1));  // Ref count is 1
  13.     SmartPointer<int> p2(new int(2));  // Ref count is 1
  14.     SmartPointer<int> p3(new int(3));  // Ref count is 1
  15.     SmartPointerArr<int> pArr(new int[100]);  // Ref count is 1
  16.  
  17.     cout << *p1; // Read p1’s value – 1
  18.     cout << *p2; // Read p2’s value – 2
  19.     cout << *p3; // Read p3’s value – 3
  20.  
  21.     // Assign p2 to p1
  22.     // p2’s data’s refcount is down to 0. p2’s data is automatically deleted.
  23.     // Assign p1’s data to p2.
  24.     // P1’s data’s refcount is incremented to 2.
  25.     p2 = p1;
  26.  
  27.     cout << *p1; // Read p1’s value – 1
  28.     cout << *p2; // Read p2’s value – 1
  29.     cout << *p3; // Read p3’s value – 3
  30.  
  31.     // Inside this function, p1’s refcount is now 3
  32.     TestFunc( p1 );
  33.     // P1’s refcount is now back to 2 again.
  34.  
  35.     // When it falls here, all smart pointers gets their refcount decremented to 0 then destroyed.
  36.     // pArr’s refcount – 1 = 0 – destroy using vector delete
  37.     // p3’s refcount – 1 = 0 – destroyed using scalar delete
  38.     // p2’s refcount – 1 = 1 – not yet destoyed
  39.     // p1’s refcount – 1 = 0 – now 0 so delete.
  40.  
  41.     // Zero Memory leaks!!!
  42. }

 

Here’s the source code:
http://codaset.com/codesushi/pulse-tec/source/master/blob/Source/Engine/Include/SmartPointer.h
http://codaset.com/codesushi/pulse-tec/source/master/blob/Source/Engine/Include/SmartPointer.inl

For more info on smart pointers search for shared_ptr.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s