You should probably read the book Alexandrescu.
As for the local static, I have not used Visual Studio for some time, but when compiling with Visual Studio 2003 there was one local static resource assigned to a DLL ... talking about a debugging nightmare, I will remember that one for a while: /
1. Singleton's lifetime
The main question about singles is life cycle management.
If you are ever trying to use an object, you need to be alive and kick. Thus, the problem arises from both initialization and destruction, which is a common problem in C ++ with global variables.
Initialization is usually the easiest thing to fix. As both methods show, it is simple enough to initialize on first use.
The destruction is a little more subtle. global variables are destroyed in the reverse order in which they were created. Therefore, in the local static case, you actually do not control things.
2. Local static
struct A { A() { B::Instance(); C::Instance().call(); } }; struct B { ~B() { C::Instance().call(); } static B& Instance() { static B MI; return MI; } }; struct C { static C& Instance() { static C MI; return MI; } void call() {} }; A globalA;
What is the problem? Let the order in which the constructors and destructors be called be checked.
First, the construction phase:
- Performed
A globalA; , A::A() is calledA::A() calls B::B()A::A() calls C::C()
It works great because we initialize instances of B and C on first access.
Secondly, the phase of destruction:
C::~C() is called because it was the last one built from 3B::~B() is called ... oups, it is trying to access the C instance!
Thus, we have undefined behavior upon destruction, hum ...
3. New strategy
The idea here is simple. global inline elements are initialized before other global variables, so your pointer will be set to 0 before any of the code you write is called, it ensures that the test:
S& S::Instance() { if (MInstance == 0) MInstance = new S(); return *MInstance; }
Will check if the instance is truly correct.
However, as has been said, there is a memory leak and the worst destructor that is never called. The solution exists and is standardized. This is a call to the atexit function.
The atexit allows you to specify the action to take when the program shuts down. With this, we can write singleton:
// in s.hpp class S { public: static S& Instance(); // already defined private: static void CleanUp(); S(); // later, because that where the work takes place ~S() { /* anything ? */ } // not copyable S(S const&); S& operator=(S const&); static S* MInstance; }; // in s.cpp S* S::MInstance = 0; S::S() { atexit(&CleanUp); } S::CleanUp() { delete MInstance; MInstance = 0; } // Note the = 0 bit!!!
First, let me learn more about atexit . Signature int atexit(void (*function)(void)); , that is, it takes a pointer to a function that takes nothing as an argument and returns nothing.
Secondly, how does it work? Well, exactly the same as in the previous use case: upon initialization, it creates a stack of pointers to call the function, and upon destruction, it stacks the stack one element at a time. Thus, functions are called in Last-In First-Out mode.
What's going on here?
First Access Design (initialization is fine), I register the CleanUp method for exit time
Exit time: CleanUp method is called. It destroys the object (so we can do the work in the destructor efficiently) and reset the pointer to 0 to signal this.
What happens if (for example, in the example with A , B and C ) I call an instance of an already destroyed object? Well, in this case, since I returned the pointer to 0 , I will rebuild the temporary singleton and the loop will start again. He will not live long, as I delete my stack.
Alexandrescu called it Phoenix Singleton , as he resurrects from his ashes, if needed after his destruction.
Another alternative is to have a static flag and set it to destroyed during cleaning, and let the user know that he did not receive an instance of the singleton, for example, returning a null pointer. The only problem I'm returning the pointer (or link) with is that you better hope that no one is so stupid as to call delete on it: /
4. Monoid pattern
Since we're talking about Singleton , I think it's time to introduce the Monoid template. In fact, this can be seen as a degenerate case of the Flyweight template or the use of Proxy over Singleton .
The Monoid pattern Monoid simple: all instances of the class have a common state.
I will take the opportunity to expose the implementation of non-Phoenix :)
class Monoid { public: void foo() { if (State* i = Instance()) i->foo(); } void bar() { if (State* i = Instance()) i->bar(); } private: struct State {}; static State* Instance(); static void CleanUp(); static bool MDestroyed; static State* MInstance; };
What is the use? It hides the fact that the state is being split; it hides Singleton .
- If you ever need 2 different states, you may be able to do this without changing each line of the code used (instead of
Singleton by calling Factory ) - Nodoby is going to call
delete on your singleton instance, so that you really manage the state and prevent accidents ... you still can't do it against malicious users! - You control access to the singleton, so if you call it after it is destroyed, you can handle it correctly (do nothing, register, etc.)
5. The last word
Whatever it may seem, I would like to indicate that I was happy to look at multithreaded problems ... read Alexandrescu Modern C ++ to find out more!