Singleton template in C ++

I have a question about a singleton pattern.

I saw two cases regarding a static member in a singleton class.

At first it’s an object like this

class CMySingleton { public: static CMySingleton& Instance() { static CMySingleton singleton; return singleton; } // Other non-static member functions private: CMySingleton() {} // Private constructor ~CMySingleton() {} CMySingleton(const CMySingleton&); // Prevent copy-construction CMySingleton& operator=(const CMySingleton&); // Prevent assignment }; 

One of them is a pointer, for example

 class GlobalClass { int m_value; static GlobalClass *s_instance; GlobalClass(int v = 0) { m_value = v; } public: int get_value() { return m_value; } void set_value(int v) { m_value = v; } static GlobalClass *instance() { if (!s_instance) s_instance = new GlobalClass; return s_instance; } }; 

What is the difference between the two cases? Which one is correct?

+46
c ++ singleton
Mar 23 '10 at 1:16
source share
8 answers

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 called
  • A::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 3
  • B::~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; }; // .cpp bool Monoid::MDestroyed = false; State* Monoid::MInstance = 0; State* Monoid::Instance() { if (!MDestroyed && !MInstance) { MInstance = new State(); atexit(&CleanUp); } return MInstance; } void Monoid::CleanUp() { delete MInstance; MInstance = 0; MDestroyed = true; } 

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!

+60
Mar 23 '10 at 8:26
source share
β€” -

More truly another. I would try to avoid using Singleton altogether, but when I came across the thought that this was the way to go, I used both of these and they worked great.

One device with a pointer option is a memory leak. On the other hand, your first example may be destroyed before you finish with it, so you will fight, regardless of whether you want to choose a more suitable owner for this thing, which can create and destroy it at the right time .

+4
Mar 23 '10 at 1:21
source share

The difference is that the second is a memory leak (singleton itself), and the first is not. Static objects are initialized after the first call of their associated method and (until the program crashes), they are destroyed before the program exits. The version with the pointer will leave a pointer allocated when the program exits, and memory checks like Valgrind will complain.

In addition, what prevents someone from doing delete GlobalClass::instance(); ?

For the above two reasons, the static version is the more common method and the one prescribed in the original Design Patterns book.

+2
Mar 23 '10 at 1:22
source share

Use the second approach - if you do not want to use atexit to free your object, you can always use a guardian object (for example, auto_ptr or something written by itself). This can lead to release before you finish working with the object, as with the first method.

The difference is that if you use a static object, you have practically no way to check whether it is freed or not.

If you use a pointer, you can add an additional static bool to indicate whether a singleton has already been destroyed (as in Monoid). Then your code can always check if one of them has already been destroyed, and although you may not fulfill what you intend to intend, at least you will not get a cryptic β€œsegmentation error” or β€œaccess violation”, and the program will avoid abnormal completion.

+1
Jun 12 2018-11-12T00:
source share

I agree with Billy. In the second approach, we dynamically allocate memory from the heap using a new one . This memory always remains and is never freed unless a delete call is made. Therefore, the Global pointer approach creates a memory leak.

 class singleton { private: static singleton* single; singleton() { } singleton(const singleton& obj) { } public: static singleton* getInstance(); ~singleton() { if(single != NULL) { single = NULL; } } }; singleton* singleton :: single=NULL; singleton* singleton :: getInstance() { if(single == NULL) { single = new singleton; } return single; } int main() { singleton *ptrobj = singleton::getInstance(); delete ptrobj; singleton::getInstance(); delete singleton::getInstance(); return 0; } 
+1
Mar 05 '12 at 19:40
source share

Your first example is more typical for a singleton. The second example is different in that it is created upon request.

However, I will try to avoid the use of single numbers in general, since they are nothing more than global variables.

0
Mar 23 '10 at 1:21
source share

The best approach is to create a singleton class. This also avoids checking for instance availability in the GetInstance () function. This can be achieved using a function pointer.

 class TSingleton; typedef TSingleton* (*FuncPtr) (void); class TSingleton { TSingleton(); //prevent public object creation TSingleton (const TSingleton& pObject); // prevent copying object static TSingleton* vObject; // single object of a class static TSingleton* CreateInstance (void); static TSingleton* Instance (void); public: static FuncPtr GetInstance; }; FuncPtr TSingleton::GetInstance = CreateInstance; TSingleton* TSingleton::vObject; TSingleton::TSingleton() { } TSingleton::TSingleton(const TSingleton& pObject) { } TSingleton* TSingleton::CreateInstance(void) { if(vObject == NULL){ // Introduce here some code for taking lock for thread safe creation //... //... //... if(vObject == NULL){ vObject = new TSingleton(); GetInstance = Instance; } } return vObject; } TSingleton* TSingleton::Instance(void) { return vObject; } void main() { TSingleton::GetInstance(); // this will call TSingleton::Createinstance() TSingleton::GetInstance(); // this will call TSingleton::Instance() // all further calls to TSingleton::GetInstance will call TSingleton::Instance() which simply returns already created object. } 
0
03 Oct '12 at 6:22
source share

In response to complaints of "memory leak" there is an easy fix:

 // dtor ~GlobalClass() { if (this == s_instance) s_instance = NULL; } 

In other words, give the class a destructor that de-initializes the hidden pointer variable when the singleton object is destroyed during program termination.

Once you have done this, the two forms are almost identical. The only significant difference is that you return links to a hidden object, and the other returns a pointer to it.

Update

As @BillyONeal points out, this will not work because the pointer object is not deleted. Uch.

I don't like to even think about it, but you can use atexit() to do the dirty work. Sheesh.

Oh, well, never mind.

-one
Mar 23 '10 at 1:45
source share



All Articles