The Singleton mode is one of the common design patterns, but it is not easy to implement a practical design pattern.
Standard Implementation
1 class Singleton
2 {
3 public:
4 static Singleton * Instance ()
5 {
6 if (0 = _ instance)
7 {
8 _ instance = new Singleton;
9}
10 return _ instance;
11}
12 protected:
13 Singleton (void)
14 {
15}
16 virtual ~ Singleton (void)
17 {
18}
19 static Singleton * _ instance;
20 };
This is the method used in textbooks. It seems that there are no problems, but there are actually many problems. Next we will solve it one by one.
Question 1: automatic garbage collection
The above program must remember to release the memory at the end of the program. To let it automatically release the memory, we introduce auto_ptr to change it.
1 # include <memory>
2 # include <iostream>
3 using namespace std;
4 class Singleton
5 {
6 public:
7 static Singleton * Instance ()
8 {
9 if (0 = _ instance. get ())
10 {
11 _ instance. reset (new Singleton );
12}
13 return _ instance. get ();
14}
15 protected:
16 Singleton (void)
17 {
18 cout <"Create Singleton" <endl;
19}
20 virtual ~ Singleton (void)
21 {
22 cout <"Destroy Singleton" <endl;
23}
24 friend class auto_ptr <Singleton>;
25 static auto_ptr <Singleton> _ instance;
26 };
27 // Singleton. cpp
28 auto_ptr <Singleton> Singleton: _ instance;
Question 2 Add a template
In one of my projects, there are multiple Singleton classes. For Singleton classes, I want to implement all the above, which makes me feel bored. So I thought of a template to complete these duplicates.
Repeat the work.
Now we want to add the most attractive single-piece implementation in this article:
1 # include <memory>
2 using namespace std;
3 using namespace C2217: Win32;
4
5 namespace C2217
6 {
7 namespace Pattern
8 {
9 template <class T>
10 class Singleton
11 {
12 public:
13 static inline T * instance ();
14
15 private:
16 Singleton (void ){}
17 ~ Singleton (void ){}
18 Singleton (const Singleton &){}
19 Singleton & operator = (const Singleton &){}
20
21 static auto_ptr <T> _ instance;
22 };
23
24 template <class T>
25 auto_ptr <T> Singleton <T>: _ instance;
26
27 template <class T>
28 inline T * Singleton <T>: instance ()
29 {
30 if (0 = _ instance. get ())
31 {
32 _ instance. reset (new T );
33}
34
35 return _ instance. get ();
36}
37
38 // Class that will implement the singleton mode,
39 // must use the macro in it's delare file
40 # define DECLARE_SINGLETON_CLASS (type )\
41 friend class auto_ptr <type> ;\
42. friend class Singleton <type>;
43}
44}
Question 3 thread security
The above program can adapt to a single-threaded program. However, a problem occurs if you use a multi-threaded program. The main problem is to execute _ instance. reset (new T) at the same time );
Two new objects will be generated at the same time, and one will be released immediately, which is inconsistent with the intention of the Singleton mode. Therefore, you need a safer version:
1 # include <memory>
2 using namespace std;
3 # include "Interlocked. h"
4 using namespace C2217: Win32;
5
6 namespace C2217
7 {
8 namespace Pattern
9 {
10 template <class T>
11 class Singleton
12 {
13 public:
14 static inline T * instance ();
15
16 private:
17 Singleton (void ){}
18 ~ Singleton (void ){}
19 Singleton (const Singleton &){}
20 Singleton & operator = (const Singleton &){}
21
22 static auto_ptr <T> _ instance;
23 static CResGuard _ rs;
24 };
25
26 template <class T>
27 auto_ptr <T> Singleton <T>: _ instance;
28
29 template <class T>
30 CResGuard Singleton <T >:_ rs;
31
32 template <class T>
33 inline T * Singleton <T>: instance ()
34 {
35 if (0 = _ instance. get ())
36 {
37 CResGuard: CGuard gd (_ rs );
38 if (0 = _ instance. get ())
39 {
40_instance. reset (new T );
41}
42}
43 return _ instance. get ();
44}
45
46 // Class that will implement the singleton mode,
47 // must use the macro in it's delare file
48 # define DECLARE_SINGLETON_CLASS (type )\
49 friend class auto_ptr <type> ;\
50 friend class Singleton <type>;
51}
52}
The main function of the CresGuard class is thread access synchronization. The Code is as follows:
1 class CResGuard {
2 public:
3 CResGuard () {m_lGrdCnt = 0; InitializeCriticalSection (& m_cs );}
4 ~ CResGuard () {DeleteCriticalSection (& m_cs );}
5
6 // IsGuarded is used for debugging
7 BOOL IsGuarded () const {return (m_lGrdCnt> 0 );}
8
9 public:
10 class CGuard {
11 public:
12 CGuard (CResGuard & rg): m_rg (rg) {m_rg.Guard ();};
13 ~ CGuard () {m_rg.Unguard ();}
14
15 private:
16 CResGuard & m_rg;
17 };
18
19 private:
20 void Guard () {EnterCriticalSection (& m_cs); m_lGrdCnt ++ ;}
21 void Unguard () {m_lGrdCnt --; LeaveCriticalSection (& m_cs );}
22
23 // Guard/Unguard can only be accessed by the nested CGuard class.
24 friend class CResGuard: CGuard;
25
26 private:
27 CRITICAL_SECTION m_cs;
28 long m_lGrdCnt; // # of EnterCriticalSection cballs
29 };
Question 4 practical methods
For example, if you have a class that needs to implement the single-piece mode, you should implement it like this:
1 # include "singleton. h"
2 using namespace C2217: Pattern;
3
4 class ServiceManger
5 {
6 public:
7 void Run ()
8 {
9}
10 private:
11 ServiceManger (void)
12 {
13}
14 virtual ~ ServiceManger (void)
15 {
16}
17 DECLARE_SINGLETON_CLASS (ServiceManger );
18 };
19
20 typedef Singleton <ServiceManger> SSManger;
It is very simple to use, and it is no different from the general Singleton implementation method.
1 int _ tmain (int argc, _ TCHAR * argv [])
2 {
3 SSManger: instance ()-> Run ();
4}
The implementation of a simple Singleton mode shows the rich semantics hidden behind the C ++ language. I hope someone can implement a better Singleton for everyone to learn.