Search code examples
c++dllsingletonin-memorymake-shared

Singleton - Impl. using static class member vs. static member variable


Bring you two different implantation of singleton design pattern:

static class member

class SingletonCSM;
using SingletonCSMSp = std::shared_ptr < SingletonCSM > ;

class SingletonCSM
{
public:

    ~SingletonCSM()  { spInstance = nullptr; }

    static SingletonCSMSp GetInstance()
    {
        if (!spInstance)
            spInstance = std::make_shared<SingletonCSM>();

        return spInstance;
    }

private:

    SingletonCSM() {}

    // will be intilized in the cpp: SingletonCSMSp SingletonCSM::spInstance = nullptr;
    static SingletonCSMSp spInstance; 
};

static member variable

class SingletonFSV;
using SingletonFSVSp = std::shared_ptr < SingletonFSV > ;

class SingletonFSV
{
public:

    ~SingletonFSV() {}

    static SingletonFSVSp GetInstance()
    {
        static SingletonFSVSp spInstance = std::make_shared<SingletonFSV>();
        return spInstance;
    }

private:

    SingletonFSV() {}

};

I always use the first impl. SingletonCSM. I came across, in our code, an impl. like SingletonFSV

Questions

  1. Can we consider both impl. as a valid impl. of the design pattern?
  2. Are both, functionally, the same?

Motivation

Background

Class SingletonFSV was implemented as a part of a DLL project. This DLL, compiled in VS 2013, is loaded in memory by an exe file and run.

Problem

I've upgrade my VS to VS 2015, compiled the DLL project and run the exe. All of a sudden, it crashed. While debugging, I've notice that the crash happened in the DLL itself. make_shared called withing GetInstance() returned nullptr and naturally caused segmentation fault.

Solution

I've changed SingletonFSV impl. to SingletonCSM and the crash stopped. make_shared returned a valid pointer and the problem was solved.

Question

I just don't understand what was the problem and why was it solved?


Solution

  • When you put a static variable inside a function it is created the first time the function is called so it is guaranteed to have been instantiated to all callers of the function.

    Members that are declared static could be instantiated before or after you call your function because the initialization order between translation units is undefined. So a global object or static object could try to access a static member before its been initialized by the runtime system.

    So to your questions:

    1. Can we consider both impl. as a valid impl. of the design pattern?
    2. Are both, functionally, the same?

    No. Using a static member is dangerous because a caller to SingletonCSM::GetInstance() can access a non-created object if the nullptr initialization has not taken place before the call. The static function method is the recommended method because it guarantees initialization has completed for every caller.

    I just don't understand what was the problem and why was it solved?

    In your case moving to the more dangerous method appears to have stopped your crash. I can not explain why that is. But you may not have removed the problem, it may be that you have undefined behavior elsewhere that has simply stopped manifesting in this case but that may resurface later with a different change.