template <typename T>
class SharedPtr {
    T*   ptr;    // the underlying pointer
    int* count;  // the reference count
public:
    explicit SharedPtr(T* p = nullptr) : ptr{p}, count{new int{1}} {}
    ~SharedPtr() {
        if (--*count == 0) {
            delete count;
            delete ptr;
        }
    }

    SharedPtr(const SharedPtr<T>& sp) : ptr(sp.ptr), count(sp.count) {
        ++*count; 
    }
    SharedPtr<T>& operator=(const SharedPtr<T>& sp) {
        if (this != &sp) {
            // first, detach.
            if (--*count == 0) {
                delete count;
                delete ptr;
            }
            // attach to the new object.
            ptr = sp.ptr;
            count = sp.count;
            ++*count;
        }
        return *this;
    }

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

    operator void*() const { return ptr; } // enables "if (sp) ..."
    T* get_ptr() const { return ptr; } // access to underlying ptr
    int get_count() const { return *count; } // access to reference count
};
