Simple shared pointer implementation in a single threaded environment (revised) std::shared_ptr does not stop you from having the same resource managed by multiple control-blocks (and thus independent sets of shared-pointers), even though it is illegal and leads to double-deletes. Additionally, atomic reference-counts don't have any advantage in single-threaded environments but are still more expensive. Thus I wrote the following version to avoid these problems. 

(This is a follow-up to: simple shared pointer implementation in single threaded environment)

template &lt;typename T&gt;
struct ThreadUnsafeSharedPtr {
private:
    struct Pair {
        T data;
        size_t cnt;
        template &lt;typename ... Args&gt;
        Pair(Args &amp;&amp; ... args): data(std::forward&lt;Args&gt;(args)...), cnt(1) {}
    };
    Pair *ptr;

public:
    template &lt;typename ... Args&gt;
    static ThreadUnsafeSharedPtr make(Args &amp;&amp; ... args) {
        ThreadUnsafeSharedPtr p;
        p.ptr = new Pair(std::forward&lt;Args&gt;(args)...);
        return p;
    }

    ThreadUnsafeSharedPtr() : ptr(nullptr) {}

    ThreadUnsafeSharedPtr(const ThreadUnsafeSharedPtr &amp; other) : ptr(other.ptr) {
        if (ptr &amp;&amp; &amp;other != this) {
            ++(ptr-&gt;cnt);
        }
    }
    ThreadUnsafeSharedPtr &amp; operator=(const ThreadUnsafeSharedPtr &amp; other) {
        if (ptr &amp;&amp; &amp;other != this) {
            ptr = other.ptr;
            ++(ptr-&gt;cnt);
        }
        return *this;
    }
    ThreadUnsafeSharedPtr(ThreadUnsafeSharedPtr &amp;&amp; other) : ptr(other.ptr) {
        if (&amp;other != this) {
            other.ptr = nullptr;
        }
    }
    ThreadUnsafeSharedPtr &amp; operator=(ThreadUnsafeSharedPtr &amp;&amp; other) {
        if (&amp;other != this) {
            ptr = other.ptr;
            other.ptr = nullptr;
        }
        return *this;
    }

    ~ThreadUnsafeSharedPtr() {
        if (ptr) {
            if(--ptr-&gt;cnt == 0) {
                delete ptr;
            }
        }
    }
    T &amp; operator* () {
        return ptr-&gt;data;
    }

    T * operator-&gt; () {
        return &amp;(ptr-&gt;data);
    }
};
Unless this is just for learning, always remember that adding yet another smart-pointer will seriously hinder interaction with anyone expecting a different one.

The Design:


The maker-function is mandatory:


Cannot use a different allocator.
Cannot generally be used to allow sharing across DLL boundaries, as DLLs (in contrast to SOs) don't do symbol unification.
Cannot be used with already-allocated resources, or those needing different deallocation.

Your shared-pointers must always point to the full object. Interior pointers are not allowed, nor are base-pointers.
You don't allow for weak pointers at all.
As an aside, the overhead of using atomic reference-counts is small enough that the standard library eschews the complication of adding single-threaded shared-pointers. Consider whether that's really the part you should save on.


The Implementation:


Using in-class-initializers allows you to simplify the ctors.
You could use aggregate-initialization with Pair, no need for a custom ctor.
Explicitly testing for self-assignment is a bad idea, as it pessimises the common case. Anyway, it's completely useless in a ctor!
You should know that -&gt; binds stronger than anything but scope-resolution ::. If you need to, there are many places to look up the operator precedence rules.
Your move-assignment-operator leaks the assignee's state. Fix it by just doing an unconditional swap instead.