Master/Slave smart-pointer I'm currently working on a project where i need to access objects from various places, pretty much like std::shared_ptr. At a certain point (e.g. when a library is unloaded) i need to destruct these objects. It is crucial to the project that the object is really destructed at this point, i can not just hand out weak_ptr and rely that no one keeps a shared_ptr. If i would just hand out weak_ptr to other parts of the program, anyone could call lock() and store the shared_ptr, delaying or preventing destruction of the objects. This would crash my program.

What happens in the background is this: I have a small number of classes providing generic access to yet unknown types, e.g. a class Object providing a method virtual getChilds()=0;. Libraries might then inherit from those classes with a class template &lt;O&gt; class TemplateObject; implementing the pure virtual functions. This way other parts of the program, including a GUI, can access in a limited way objects of a type, which was unknown at compile time. But if i destruct the Object with its virtual destructor, i call code that lives in the library - which obviously fails if i have already unloaded the library. I therefore need to make sure that the Object is destructed when the library is unloaded. The GUI e.g. is then able to notice that the object is already gone and can no longer be used.

My Idea was to create two smart pointers, namely master_ptrand slave_ptr. The master_ptr behaves pretty much like a unique_ptr or like "the one and only" shared_ptr, destructing the managed object. The slave_ptr acts much like a weak_ptr, just allowing a more direct access and hiding the option to get back the shared_ptr. The managed objects would be non-copyable and non-movable to make sure there are no copies left somewhere.

My implementation looks like this:

class ObjectInvalid{};

template &lt;typename O&gt;
class slave_ptr {
private:
    std::weak_ptr&lt;O&gt; m_ptr;

public:
    slave_ptr() {}
    slave_ptr(std::shared_ptr&lt;O&gt; ptr) : m_ptr(ptr) {}

    O&amp; operator*() const {
        if (m_ptr.expired())
            throw ObjectInvalid();

        return m_ptr.lock().operator*();
    }

    O* operator-&gt;() const {
        if (m_ptr.expired())
            throw ObjectInvalid();

        return m_ptr.lock().operator-&gt;();
    }

    void reset() {
        m_ptr.reset();
    }
};


template &lt;typename O&gt;
class master_ptr {
private:
    std::shared_ptr&lt;O&gt; m_ptr;
public:
    master_ptr() = default;
    master_ptr(O* ptr) : m_ptr(ptr) {}

    //non-copyable
    master_ptr(const master_ptr&amp;) = delete;
    master_ptr&amp; operator=(const master_ptr&amp;) = delete;

    //movable
    master_ptr(master_ptr&amp;&amp; other) = default;
    master_ptr&amp; operator=(master_ptr&amp;&amp; other) = default;

    O&amp; operator*() const {
        if (!m_ptr)
            throw ObjectInvalid();

        return m_ptr.operator*();
    }

    O* operator-&gt;() const {
        if (!m_ptr)
            throw ObjectInvalid();

        return m_ptr.operator-&gt;();
    }

    //allows reset before going out of scope
    void reset() {
        m_ptr.reset();
    }

    //cast to slave
    operator slave_ptr&lt;O&gt;() {
        return slave_ptr&lt;O&gt;(m_ptr);
    }
};


A small test tells me it works as i would expect, but before implementing it in a bigger project i wanted to aks here if theres any flaws i didn't see right now or if you could hint me to some improvements.

About priorities: Easy use and safety (no possible memory leaks, objects destructed when needed, exceptions thrown afterwards) are top priorities here. Performance is a "nice to have".
This is decidedly shady:

O&amp; operator*() const {
    if (m_ptr.expired())
        throw ObjectInvalid();

    return m_ptr.lock().operator*();
}

O* operator-&gt;() const {
    if (m_ptr.expired())
        throw ObjectInvalid();

    return m_ptr.lock().operator-&gt;();
}


In the line:

return m_ptr.lock().operator-&gt;();


You create a shared pointer. Get the pointer value from the shared pointer. Destroy the shared pointer then return the pointer that was in the shared pointer.

So now you have a pointer but no guarantee that the object it points at exists anymore (as you destroyed your copy of the shared pointer).

There are a couple of operations on the master pointer that you don't support.


You can't test if it is empty.
So you are playing Russian roulette every time you use it.
You can't use it as part of a conditional (ie no conversion to bool).
You don't have a constructor that takes a nullptr => std::nullptr_t
You have a couple of problems with implicit construction.