Custom std::shared_ptr implementation I'd like to get a few pointers about my code.
I know that shared_ptr is up and running, and I'm reinventing the wheel but you can't say no to the school assignment.

template &lt;typename T&gt; class SharedPointer;

template &lt;typename T&gt;
void swap(SharedPointer&lt;T&gt; &amp;lhs, SharedPointer&lt;T&gt; &amp;rhs)
{
    lhs.swap(rhs);
}

template &lt;typename T&gt;
class SharedPointer {
    friend void swap&lt;T&gt;(SharedPointer &amp;lhs, SharedPointer &amp;rhs);

public:
    SharedPointer(): use_c(new std::size_t(0)), p(nullptr), deleter(Deleter()) { }
    SharedPointer(T *ptr): p(ptr), use_c(new std::size_t(1)), deleter(Deleter()) { }
    SharedPointer(T *ptr, const std::function&lt;void(*)&gt; &amp;del):
            p(ptr), use_c(new std::size_t(1)), deleter(del) { }
    SharedPointer(const SharedPointer &amp;rhs): 
            use_c(rhs.use_c), p(rhs.p), deleter(rhs.deleter) { ++*use_c; }

    SharedPointer(SharedPointer &amp;&amp;) noexcept;
    SharedPointer&amp; operator=(const SharedPointer &amp;);
    SharedPointer&amp; operator=(SharedPointer &amp;&amp;) noexcept;
    ~SharedPointer() { free(); }

    std::size_t use_count() { return *use_c; }

    bool unique() const { return *use_c == 1; }

    operator bool() const { return p != nullptr; }

    void reset() noexcept { free(); use_c = new std::size_t(0); };
    void reset(T*);
    void reset(T*, const std::function&lt;void(T*)&gt;&amp;);

    void swap(SharedPointer&lt;T&gt;&amp;);

    T* get() const { return p; }

    T&amp; operator*() const { return *p; }
    T* operator-&gt;() const { return &amp;*p; }

private:
    std::size_t *use_c;
    T *p;
    std::function&lt;void(T*)&gt; deleter;

    void free();
};

template &lt;typename T&gt;
SharedPointer&lt;T&gt;::SharedPointer(SharedPointer &amp;&amp;rhs) noexcept:
        use_c(rhs.use_c), p(rhs.p), deleter(std::move(rhs.deleter))
{
    rhs.use_c = new std::size_t(0);
    rhs.p = nullptr;
}

template &lt;typename T&gt;
SharedPointer&lt;T&gt; &amp;SharedPointer&lt;T&gt;::operator=(const SharedPointer &amp;rhs)
{
    ++*rhs.use_c;
    free();
    this-&gt;use_c = rhs.use_c;
    this-&gt;p = rhs.p;
    this-&gt;deleter = rhs.deleter;
    return *this;
}

template &lt;typename T&gt;
SharedPointer&lt;T&gt; &amp;SharedPointer&lt;T&gt;::operator=(SharedPointer &amp;&amp;rhs) noexcept
{
    swap(rhs);
    rhs.free();
    rhs.use_c = new std::size_t(0);
    return *this;
}

template &lt;typename T&gt;
void SharedPointer&lt;T&gt;::reset(T *rhs)
{
    if (p != rhs) {
        free();
        p = rhs;
        use_c = new std::size_t(1);
    }
}

template &lt;typename T&gt;
void SharedPointer&lt;T&gt;::reset(T *rhs, const std::function&lt;void(T *)&gt; &amp;del)
{
    if (p != rhs) {
        reset(rhs);
        deleter = del;
    }
}

template &lt;typename T&gt;
void SharedPointer&lt;T&gt;::swap(SharedPointer&lt;T&gt; &amp;rhs)
{
    using std::swap;
    swap(this-&gt;use_c, rhs.use_c);
    swap(this-&gt;p, rhs.p);
    swap(this-&gt;deleter, rhs.deleter);
}

template &lt;typename T&gt;
void SharedPointer&lt;T&gt;::free()
{
    if (p &amp;&amp; --*use_c == 0) {
        deleter(p);
        delete use_c;
    } else if (!p) {
        delete use_c;
    }
    use_c = nullptr;
    p = nullptr;
}


I've yet to add here some non-member operators, but I guess that's trivial and it's no use to litter this post so much.
I've tried to test it here and there and it looks like it works but I'm unsure about copy-and-swap and few other things.
It uses this functor class as the default deleter, if anyone is wondering what's that about.  

class Deleter {
public:
    template &lt;typename T&gt; void operator()(T *p) const;
};

template &lt;typename T&gt; void Deleter::operator()(T *p) const
{
    delete p;
}


So basically I'd like to ask your help with the copy-and-swap idiom - if this implementation is correct and if it's needed at all and the overall coding style.
Nice first post!

There are things that std::unique_ptr provides which are absent here, notably the aliasing constructor (so you can own the containing object of what's referenced) and weak pointers (so cached values don't cause unwanted object retention) but otherwise, this is pretty good.

Omissions

I had a couple of compilation errors, because it's missing #include &lt;functional&gt;.  Also, the deleter ought to have a specialization for array types (which need delete[] p; instead of delete p;).

Naming

On naming, I'd probably choose something like release() for what you've called free() - but that's probably just because of half a lifetime writing C.

Copy and swap

You mention the copy-and-swap idiom; this is how you'd use it for your assignment operator:

template&lt;typename T&gt;
SharedPointer&lt;T&gt;&amp; SharedPointer&lt;T&gt;::operator=(SharedPointer rhs)
{
    swap(rhs);
    return *this;
}


Notice that we pass rhs by value, which means we're getting the copy constructor to do all the work for us.  Having swapped, rhs has our old value, which is then destructed at the end of the function, so we don't have to duplicate the work of the constructor.

Move-assignment

Move assignment doesn't need to reset rhs - just perform the swap, and expect the moved-from value to be destructed soon:

template &lt;typename T&gt;
SharedPointer&lt;T&gt; &amp;SharedPointer&lt;T&gt;::operator=(SharedPointer &amp;&amp;rhs) noexcept
{
    swap(rhs);
    return *this;
}


Swap

The swap function doesn't need to be a friend, as it uses only the public interface of the shared pointer.

Use default arguments to simplify

You can reduce the number of constructors, like this:

SharedPointer(T *ptr = nullptr,
              const std::function&lt;void(*)&gt; &amp;del = Deleter())
    : p(ptr),
      use_c(new std::size_t(ptr!=nullptr)),
      deleter(del)
{ }


This is also true for the reset() method.

Reset should change deleter

The condition in reset() skips changing the deleter if there's no change to the pointer.  But we might be calling reset() for exactly that purpose.  Also, if a null pointer is passed as the new value, it's treated the same as a non-null pointer.  Both of these problems can be avoided by using swap() and default arguments (to re-use our constructor and destructor):

    // declaration
    void reset(T* = nullptr, const std::function&lt;void(T*)&gt;&amp; = Deleter());

template &lt;typename T&gt;
void SharedPointer&lt;T&gt;::reset(T *rhs, const std::function&lt;void(T*)&gt;&amp; del)
{
    swap(SharedPointer(rhs, del));
}