Search code examples
c++shared-ptr

Private constructor and make_shared


I have a singleton class with a private constructor. In the static factory method I do the following:

shared_ptr<MyClass> MyClass::GetInstance()
{
    static once_flag onceFlag;

    call_once(onceFlag, []() {
        if (_instance == nullptr)
            _instance.reset(new MyClass());
    });

    return _instance;
}

If I use

_instance = make_shared<MyClass>();

the code does not compile. My question is: why new can invoke a private constructor but make_shared not?


Solution

    1. As mentioned, std::make_shared or its component parts don't have access to private members.

    2. the call_once and once_flag are un-necessary. They are implicit in c++11 static initialisation,

    3. You normally would not want to expose the shared pointer.

     

    class MyClass
    {
        MyClass() {}
    
    public:
        static MyClass& GetInstance()
        {
            static auto instance = MyClass();
            return instance;
        }
    };
    

    However, there is one case I can imagine where you would want to expose a shared pointer to the impl - this is in the case where the class can choose to 'break off' or 'reset' the impl to a new one. In this case I would consider code like this:

    class MyClass2
    {
        MyClass2() {};
    
        static auto& InternalGetInstance()
        {
            static std::shared_ptr<MyClass2> instance { new MyClass2 };
            return instance;
        }
    
    public:
    
        static std::shared_ptr<MyClass2> GetInstance()
        {
            return std::atomic_load(std::addressof(InternalGetInstance()));
        }
    
        static void reset() {
            std::atomic_store(std::addressof(InternalGetInstance()),
                            std::shared_ptr<MyClass2>(new MyClass2));
    
        }  
    };
    

    However, in the end, it is my view that 'staticness' of a class should be an implementation detail, and unimportant to the user of the class:

    #include <memory>
    #include <utility>
    
    class MyClass
    {
        // internal mechanics
    
        struct Impl {
    
            auto doSomething() {
                // actual implementation here.
            }
        };
    
        // getImpl now becomes the customisation point if you wish to change the
        // bahviour of the class later
        static Impl& getImpl() {
            static auto impl = Impl();
            return impl;
        }
    
    
        // use value semantics - it makes for more readable and loosely-coupled code
    public:
        MyClass() {}
    
        // public methods defer to internal implementation
    
        auto doSomething() {
            return getImpl().doSomething();
        }
    };
    
    
    int main() {
    
        // note: just create objects
        auto mc = MyClass();
        mc.doSomething();
    
        // now we can pass the singleton as an object. Other functions don't even
        // need to know it's a singlton:
    
        extern void somethingElse(MyClass mc);
        somethingElse(mc);
    }
    
    void somethingElse(MyClass mc)
    {
    
    }