0

Is there any difference or specific advice when it comes to the following approaches for defining singletons?

In 1, the singleton object is a class private static, but in 2, it's a file static.

Note: m_initedObj1 is just there to show that class has state, and use case is to call this singleton->DoSomething() many times, without needing to init this object again.

1)

// header file
class Foo {
private:
    static Foo* s_fooSingleton;
    Foo();
    Obj1 m_initedObj1;

public:
    static Foo* Singleton();
    static void ClearSingleton();
    Bar DoSomething(...);
};

// cpp file
Foo* Foo::s_fooSingleton = nullptr;
Foo::Foo() { m_initedObj1 = InitObj1Somewhere(); }

/*static*/ Foo* Foo::Singleton()
{
    if(!Foo::s_fooSingleton)
        Foo::s_fooSingleton = new Foo();
    return Foo::s_fooSingleton;
}

/*static*/ void Foo::ClearSingleton()
{
    if(Foo::s_fooSingleton)
        delete Foo::s_fooSingleton;
    Foo::s_fooSingleton = nullptr;
}

Bar Foo::DoSomething(...) { // do something }

2)

// header file
class Foo {
private:
    Foo();
    Obj1 m_initedObj1;

public:
    static Foo* Singleton();
    static void ClearSingleton();
    Bar DoSomething(...);
};

// cpp file
static Foo* s_fooSingleton = nullptr;

Foo::Foo() { m_initedObj1 = InitObj1Somewhere(); }

/*static*/ Foo* Foo::Singleton()
{
    if(!s_fooSingleton)
        s_fooSingleton = new Foo();
    return s_fooSingleton;
}

/*static*/ void Foo::ClearSingleton()
{
    if(s_fooSingleton)
        delete s_fooSingleton;
    s_fooSingleton = nullptr;
} 

Bar Foo::DoSomething(...) { // do something }

2 Answers2

2

As JerryGoyal states in the comments, in 2) other methods in the same .cpp file can modify s_fooSingleton.

On the other hand, they are not both thread-safe. If you don't really mind the clearing (calling ClearSingleton() explicitly), just go with the Scott Meyers' version. Otherwise, go with the double checked locking version.

It's really hard to ensure the safety in case of explicitly deleting. You always have to check whether it's deleted before you access it. If it's a multi-threaded executable, checking and using it must be atomic, because it can be deleted just after checking.

Double checked locking could be used to create and delete the singleton, which ensures you that there is only one instance at a time. Yet, it does not ensure the object really exist, since you may accidentally delete it.

You may use smart pointers to count references and delete it if no references exist.

Or even better, see this answer https://stackoverflow.com/a/15733545/1632887.

I just wouldn't delete it explicitly if I were you!

Community
  • 1
  • 1
seleciii44
  • 1,529
  • 3
  • 13
  • 26
  • What do you mean "if you don't mind the clearing"? Wouldn't this singleton be destructed on its own? – minibigboss Apr 06 '17 at 07:41
  • i mean the ClearSingleton() function. It will be destructed in the end, but i thought that you want to call the ClearSingleton() function explicitly, which makes it even harder. – seleciii44 Apr 06 '17 at 07:48
  • 1
    No that's fine. I can remove that function and its call. Thanks! – minibigboss Apr 06 '17 at 07:51
  • just curious... how would i destruct it on demand, if i need to? – minibigboss Apr 06 '17 at 08:31
  • i tried calling Foo::Singleton().~Foo(), which did call the destructor, but afterwards, if I call Singleton(), the same object is there (and works), without calling constructor again (added a few prints in the functions to track this). – minibigboss Apr 06 '17 at 08:43
  • Calling the destructor is just calling a member function. It does not delete the object. If it's not a pointer, it will be deleted when it goes out of the scope; in our case, it's the end of your executable. And if it's a pointer, you must be sure that it won't be accessed after deletion. That means you need to count references for safety. And it keeps getting messy. I just wouldn't delete it :) – seleciii44 Apr 06 '17 at 08:55
0

Maybe this will satisfy you more:

class MyClass1 {
private:
    MyClass1(){}    
public:
    MyClass1& Instance() {
        static MyClass1 theSingleInstance;
        return theSingleInstance;
    }    
};

class MyClass2 {
private:
    MyClass2() {}    
public:
    MyClass2* Instance() {
        static MyClass2* theSingleInstance = new MyClass2;
        return theSingleInstance;
    }
};

class MyClass3 {
private:
    MyClass3() {}    
public:
    MyClass3* Instance() {
        static MyClass3 theSingleInstance;
        return &theSingleInstance;
    }
};
bobra
  • 615
  • 3
  • 18