Yet another smart pointer implementation for learning I based this implementation on CountedPtr in The C++ Standard Library book by Nicolai Josuttis (page 222) and also available online here.

I know that I can use the C++11 smart pointers but this is a learning exercise and also for some projects an earlier compiler must be used.

I didn't understand the need for the actual reference counter to be a pointer so I just used a standard integer.  Maybe that introduces a bug, not sure.  

My main concern is about the object lifecycle.  Does the reference counter as a straight integer cause a bug?  Any feedback will be much appreciated.

Here is the smart pointer class:

#ifndef SMARTPOINTER_HPP__
#define SMARTPOINTER_HPP__

#include &lt;iostream&gt;

template&lt;typename T&gt;
class sp {
public:
   sp(T* ptr) : ptr_(ptr), ref_cnt_(1) {
      std::cout &lt;&lt; "sp ctor: " &lt;&lt; ptr_ &lt;&lt; ", ref_cnt_: " &lt;&lt; ref_cnt_ &lt;&lt; std::endl;
   }

   T&amp; operator*() { return *ptr_; }

   T* operator-&gt;() { return ptr_; }

   sp(const sp&lt;T&gt;&amp; rhs) : ptr_(rhs.ptr_), ref_cnt_(rhs.ref_cnt_) {
      ++ref_cnt_;
      std::cout &lt;&lt; "sp copy ctor: " &lt;&lt; ptr_ &lt;&lt; ", ref_cnt_: " &lt;&lt; ref_cnt_ &lt;&lt; std::endl;
   }

   sp&amp; operator=(const sp&amp; rhs) {
      if(this != &amp;rhs) {
         *this = rhs;
         ++ref_cnt_;
         std::cout &lt;&lt; "sp assignment: " &lt;&lt; ptr_ &lt;&lt; ", ref_cnt_: " &lt;&lt; ref_cnt_ &lt;&lt; std::endl;
      }
      return *this;
   }

   ~sp() { 
     if(ptr_) 
       std::cout &lt;&lt; "~sp: " &lt;&lt; ptr_ &lt;&lt; ", ref count: " &lt;&lt; ref_cnt_ &lt;&lt; std::endl;
       --ref_cnt_;
       if(ref_cnt_ == 0)
         delete ptr_; 
   }

   bool operator==(const T&amp; that) const { return ptr_ == that.ptr_; }
   bool operator==(T* that) const { return ptr_ == that; }

   bool operator!=(const sp&amp; that) const { return ptr_ != that.ptr_; }
   bool operator!=(sp* that) const { return ptr_ != that; }


private:
   T* ptr_;
   unsigned ref_cnt_;
};


#endif //SMARTPOINTER_HPP__


Here is my code to exercise:

#include &lt;iostream&gt;
#include &lt;string.h&gt;

#include "smart_pointer.hpp"

class name {
public:
   name(const char* label) : label_(label) {}

   const char* get_name() const { return label_; }
   unsigned length() const { return strlen(label_); }

private:
  const char* label_;
};

int main() {

   {
      std::cout &lt;&lt; "Test with ints\n";
      sp&lt;int&gt; myptr1 = new int(3);  
      *myptr1 = 4;
      std::cout &lt;&lt; "number: " &lt;&lt; *myptr1 &lt;&lt; std::endl;

      sp&lt;int&gt; myp2(myptr1);

      sp&lt;int&gt; myp3(myptr1);

      sp&lt;int&gt; myp4 = myptr1;
   }


   {
     std::cout &lt;&lt; "Test with name object\n";
     sp&lt;name&gt; myname = new name("Andrew");
     std::cout &lt;&lt; myname-&gt;get_name() &lt;&lt; " has length: " &lt;&lt; myname-&gt;length() &lt;&lt; std::endl;
     sp&lt;name&gt; myname2(myname); 
   }

   return 0;
}


I get this output:




Test with ints
sp ctor: 004C7A80, ref_cnt_: 1
number: 4
sp copy ctor: 004C7A80, ref_cnt_: 2
sp copy ctor: 004C7A80, ref_cnt_: 2
sp copy ctor: 004C7A80, ref_cnt_: 2
~sp: 004C7A80, ref count: 2
~sp: 004C7A80, ref count: 2
~sp: 004C7A80, ref count: 2
~sp: 004C7A80, ref count: 1
Test with name object
sp ctor: 004C7A80, ref_cnt_: 1
Andrew has length: 6
sp copy ctor: 004C7A80, ref_cnt_: 2
~sp: 004C7A80, ref count: 2
~sp: 004C7A80, ref count: 1
Main problem with the reference counter:

As it stands right now, your code will eventually leak or try to delete the same pointer more than once. You are keeping a diferent reference count inside each smart pointer, which is not synchronised, so smart pointers sharing the same object can have different counts for the same memory. Take this simple example, similar to your second one:

sp&lt;MyObject&gt; ptr1 = new MyObject(); // ptr1.ref_cnt = 1
sp&lt;MyObject&gt; ptr2 = ptr1; // ptr1.ref_cnt = 1, ptr2.ref_cnt = 2
// Now we have two different counts for the same object!


Once you pass the copy constructor or assignment operator, the reference count of the original pointer is left unaltered. This cannot possibly end well. That's why in the original example you've linked the author used a pointer for the reference counter as well, so that any smart pointer sharing ownership of an object can also point to the same counter. Once the owned object is to be freed, the last pointer also frees the counter.

One cleaver trick to avoid allocating the counter in a separate new call is to allocate both the object and the counter as the same block of raw bytes, then adjust the pointers and construct the object on the remaining memory. This is how some implementations of std::make_shared() work. Once you fix the counter issue, you might want to try this optimization.

Other issues with the code:


This destructor seems broken:


   ~sp() { 
     if(ptr_) 
       std::cout &lt;&lt; "~sp: " &lt;&lt; ptr_ &lt;&lt; ", ref count: " &lt;&lt; ref_cnt_ &lt;&lt; std::endl;
       --ref_cnt_;
       if(ref_cnt_ == 0)
         delete ptr_;     
   }



Didn't you forget to wrap the first if with curly braces? This seems like a case where the indenting of the code tries to tell the reader one thing, while the code does a different thing. The first if only applied to the cout call. To avoid this confusion, it is a good idea to always provide { } even for single line control statements. So this is how I believe you intended to write, since it wouldn't make sense decrementing the counter if the pointer is null:

~sp() { 
    if(ptr_) {
        std::cout &lt;&lt; "~sp: " &lt;&lt; ptr_ &lt;&lt; ", ref count: " &lt;&lt; ref_cnt_ &lt;&lt; std::endl;
        --ref_cnt_;
        if(ref_cnt_ == 0) {
            delete ptr_; 
        }
    }
}

The comparison operators that take a raw pointer should take that pointer by const T*, to make clear that they do not attempt to modify the pointed object.
You'll probably also want to provide overloads of operator * and -&gt; that are const and return const pointer and reference. Otherwise you aren't able to call those on a const sp&lt;T&gt;.
Your class still requires a lot of tuning and polishing to be anywhere near usable. If you plan on taking it a step further, I recommend reading Modern C++ Design. The book has a dedicated chapter on Smart Pointers. It is very detailed and well written and I think it is still one of the best references out there on SPs, even though it was released before C++11.