Efficient smart pointer implementation in C++
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&lt;int&gt; sp(x);
        sp = new int(6);
    } // sp destroyed here 

    std::cout &lt;&lt; *x &lt;&lt; "\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 &lt;&lt; "deleteting " &lt;&lt; *pointer &lt;&lt; " at " &lt;&lt; pointer &lt;&lt; "\n";
        delete pointer;
        delete refs;
    }
}


This will print out "deleting 6 at &lt;some memory address&gt;". 

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 &lt;template Tp1&gt; shared_ptr(Tp1 * ...) where Tp1 is convertible to type T.
Copy constructor allowing construction from a shared_ptr with convertible template type &lt;T1&gt;. 
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&lt;, 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 (&lt;some_shared_ptr&gt;) { ... }.
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.