-1

I did some research here on stackoverflow about this topic but I didn't find a single answer from where I could learn how does a singleton class work. I already know that a singleton class can only have one instance. Could anybody explain me -or link me a tutorial- how to create the most basic singleton class and tell me line by line why/how does it work? The trouble I had is that the answers and some tutorials I looked up didn't really explain why should I declare a certain variable or function as static, a pointer, etc.

Marko Kitonjics
  • 257
  • 1
  • 3
  • 15

2 Answers2

2

Discalimer

Generally one should strive to avoid using singletons and/or global objects as much as possible. Objects that functions require should be passed explicitly. This makes it easy to test functions by passing mock objects, debug, and reduce contention when parallelizing code. For example, the only global objects in my applications are loggers (similar to the standard I/O streams) and thread-specific event loop engines.

The only reason to use a singleton or a global object is to avoid the trouble passing it through all function calls. E.g. you do not want to pass a logger object through each and every call, yet it is not necessary to create many local or member logger objects to save space and time.


Method 1

The most basic singleton is a global object initialized at dynamic initialization phase during run-time, e.g.:

// singleton.h
class Singleton { /*...*/ };
extern Singleton singleton;

// singleton.cc
#include "singleton.h"
extern Singleton singleton(/*...*/);

The benefits:

  • Initialized before main is entered.
  • Thread-safe initialization.
  • Non-intrusive, does not require the class to be a "singleton". It is object singleton what is a singleton, not the class itself.

Drawbacks:

  • Some constructor arguments may not be available before main.

Such an object can also be used before main is entered by making sure it gets initialized before the first access using Schwarz Counter (the same method std::cout and friends get initialized).


Method 2

Another option is to have a global pointer to an object. That pointer gets initialized early in main with an object created on main stack.

E.g.:

// singleton.h
class Singleton { /*...*/ };
extern Singleton* singleton;

// main.cc
int main() {
    Singleton the_singleton(/*...*/);
    singleton = &the_singleton;

    // at main end
    singleton = nullptr;
}

Benefits as those of method 1.


Method 3

Function scope static in C++11 (gcc did that before C++11) object:

// singleton.h
class Singleton { /*...*/ };
Singleton& get_singleton();

// singleton.cc
Singleton& get_singleton() {
    static Singleton singleton;
    return singleton;
}

Benefits and drawbacks as those of method 1.


Method 4

Double-checked locked singleton. Not necessary in C++11, as function scope static does thread-safe initialization for you.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • My understanding is that there should only be one instance. How does your answer demonstrate this concept? – Thomas Matthews Nov 20 '14 at 20:49
  • 1
    @ThomasMatthews There can be more than one instance of an object of singleton class, from process's point of view. If components need to access the same object they access it through the same name, e.g.: `singleton.foo()`, `singleton->foo()`, `get_singleton().foo()`. You can enforce that there is only one object of that class ever, but in my experience, that is rarely necessary. – Maxim Egorushkin Nov 20 '14 at 21:04
  • Thanks for all that! I'm trying to create a game and I want to create a class wich loads and draws all the animation sprites(images). Now if I create a simple class there is nothing stopping me (or anybody) to create more than one instance of that class, but I want only one object to handle all the drawing and loadings. I've got the code for it from a book but I just don't understand how does it work. – Marko Kitonjics Nov 20 '14 at 21:06
  • @MarkoKitonjics You should strive to pass your object explicitly to functions that need to use it. This way you can make those functions easily testable by passing a mock object and/or easily thread safe. Global objects / singletons create unnecessarily tight coupling. The only global objects in my applications are loggers (similar to the standard I/O streams) and thread-specific event loop engines. – Maxim Egorushkin Nov 20 '14 at 21:10
  • @MaximYegorushkin So you think I shouldn't make it a singleton, instead I should just create one instance of it and leave it as it is? I am working all by myself on this project but if I'd work in a team how could I make sure that nobody creates and uses 3,4 or more instance of that class unnecessarily? – Marko Kitonjics Nov 20 '14 at 21:19
  • @MarkoKitonjics I would not, because there are so many good reason not to use a singleton. – Maxim Egorushkin Nov 20 '14 at 21:37
2

Spoon boy: Do not try and singleton the class. That's impossible. bad practice. Instead... only try to realize the truth.

Neo: What truth?

Spoon boy: There is no singleton.

Neo: There is no singleton?

Spoon boy: Then you'll see, that it is not the class that is limited to one instance, it is only you that needs a single instance.

What I'm trying to say is: As a class author, never force that class to be a singleton. Just because you think the system only needs one Logger object today, doesn't mean that tomorrow you might not want two (e.g. one for the Windows Application Event Log, one for the System Event Log).

Think that your thread_pool class should be a singleton? Well, I need both a fair thread pool, and a LIFO thread pool in one of my apps.

If you are creating N Agent objects, and they all need to share one Logger object, don't write Logger as a singleton - rather use something like:

// agent.h
class Agent {
    ...
    private:
        Logger& GetLogger();
}

// agent.cpp
Logger& Agent::GetLogger() {
    static Logger TheOne;
    return The One;
}

After all, it's Agent that needs Logger to be a singleton - not Logger itself!

Community
  • 1
  • 1
Allison Lock
  • 2,375
  • 15
  • 17