# Efficient smart pointer implementation in C++

The idea behind this is mainly educational but I might even consider using it in reality if turns out to be good. Here's my first try at implementing smart pointers:

template<typename T>
class smart_pointer{
T* pointer;
std::size_t *refs;

void clear(){
if (!--*refs){
delete pointer;
delete refs;
}
}

public:
smart_pointer(T* p = NULL)
: pointer(p), refs(new std::size_t(1))
{}
smart_pointer(const smart_pointer<T>& other)
: pointer(other.pointer), refs(other.refs)
{
++*refs;
}
~smart_pointer(){
clear();
}

smart_pointer<T>& operator=(const smart_pointer<T>& other){
if (this != &other){
clear();

pointer = other.pointer;
refs    = other.refs;
++*refs;
}
return *this;
}

smart_pointer<T>& operator=(T* p){
if (pointer != p){
pointer = p;
*refs = 1;
}
return *this;
}

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

const T& operator*() const{
return *pointer;
}

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

const T* operator->() const{
return pointer;
}

std::size_t getCounts(){
return *refs;
}
};


I have tested this under valrind and its clean. Also, to see how much the "smartness" makes things slower, I did the following test:

struct foo{
int a;
};

template<typename pointer_t>
class bar{
pointer_t f_;

public:
bar(foo *f)
:f_(f)
{}

void set(int a){
f_->a = a;
}
};

int main()
{
foo *f = new foo;

typedef smart_pointer<foo> ptr_t;
//    typedef boost::shared_ptr<foo> ptr_t;
//    typedef foo* ptr_t;

bar<ptr_t> b(f);
for (unsigned int i = 0; i<300000000; ++i)
b.set(i);

//    delete f;
return 0;
}


Here is some timing between my implementation, boost, and raw pointers: (code compiled with clang++ -O3)

typedef smart_pointer<foo> ptr_t;

real   0m0.006s
user   0m0.001s
sys    0m0.002s

typedef boost::shared_ptr<foo> ptr_t;

real    0m0.336s
user    0m0.332s
sys     0m0.002s

typedef foo* ptr_t;

real    0m0.006s
user    0m0.002s
sys     0m0.003s


My implementation seems to be running almost as fast as raw pointers, which I think is a good sign. What worries me here is why Boost is running slower. Have I missed something in my implementation that is important and I might get into trouble for later?

• I have a question regarding incrementing the count. what if the smart pointer sp was originally pointed to something else, and you do a sp=sp1. In this case you need to decrease the old count first. – Peiti Li Mar 27 '15 at 7:58

So the idea behind this is mainly educational...

Cool. Always good to try and understand how things work under the covers.

...but I might even consider using it in reality if turns out to be good.

Please rethink this. Smart pointer implementations are incredibly difficult to get right. Scott Myers, of Effective C++ fame, famously tried to implement a shared_ptr. After feedback from something like 10 iterations, it was still wrong.

Let's go through a few things here.

smart_pointer(T* p = NULL)
: pointer(p), refs(new std::size_t(1))


This constructor should be explicit to avoid implicit type conversions through construction.

Your operator=(T* p) leaks. Here's a small example:

int main()
{
int *x = new int(5);
{
smart_pointer<int> sp(x);
sp = new int(6);
} // sp destroyed here

std::cout << *x << "\n"; // This should have been deleted but wasn't
}


To convince yourself that this is the case, modify your clear() method:

void clear(){
if (!--*refs){
std::cout << "deleteting " << *pointer << " at " << pointer << "\n";
delete pointer;
delete refs;
}
}


This will print out "deleting 6 at <some memory address>".

Here's just a small rundown of what boost::shared_ptr (or std::shared_ptr) provides that is missing here:

• Constructors allowing a Deleter, which is used instead of a raw delete.
• Constructor allowing construction from <template Tp1> shared_ptr(Tp1 * ...) where Tp1 is convertible to type T.
• Copy constructor allowing construction from a shared_ptr with convertible template type <T1>.
• Copy constructor taking weak_ptr, unique_ptr.
• Move constructor and assignment operator.
• Usage of nothrow where it is required to be.
• reset, which will replace the currently managed object.
• All the non-member operators like operator<, operator==, etc that allow containers and algorithms to work correctly. For example, std::set will not work correctly with your pointer class, neither will things like std::sort.
• A specialization of std::swap.
• A specialization of std::hash. Your pointer class won't work correctly with std::unordered_set or std::unordered_map.
• explicit operator bool conversion for nullptr tests, so that one can write if (<some_shared_ptr>) { ... }.
• Utility functions like std::make_shared which you should pretty much always use when creating a shared_ptr.

The big pain point, and the reason why boost::shared_ptr or std::shared_ptr will be slower is the fact that they are thread-safe. Your smart_ptr when accessed by multiple threads could very easily delete the pointer it holds before another class is done with it, leading to dereferencing of a deleted object, and thus undefined behaviour. Furthermore, all of the atomic operations are specialized for shared_ptr.

The above is not an exhaustive list.

I don't write this to discourage you, but merely to try and reinforce the fact that writing something like this is a lot of work. It is very technical, and really, really easy to get wrong. In terms of the standard library, shared_ptr may be one of the most difficult things to write correctly.

• I think we should hold an "everyone write a shared pointer day". – user1095108 Sep 5 '13 at 9:05
• I would like to add that OP's implementation allocates the reference counter on the heap separately from the subject data. This can (if used very frequently) be a source of memory fragmentation, it is generally good to avoid small heap allocations not to mention the overhead of allocating 4 bytes is like 32 bytes or something. Also if the subject data and reference counter would be allocated together in a struct one would get better data locality and improved cache behavior. These two reasons are the reasons why std::make_shared<>() exists and is preferred for creation of smart pointers. – Emily L. Feb 3 '14 at 17:10