C++单例模式

1、单例模式:
单例模式:用来创建独一无二的,只能够有一个实例的对象。 单例模式的结构是设计模式中最简单的,但是想要完全实现一个线程安全的单例模式还是有很多陷阱的。

2、应用场景:
共享数据或者共享访问点;
创建一个对象需要消耗的资源过多,如访问IO和数据库等资源;
需要定义大量的静态常量和静态方法(工具类)

单例模式的应用场景:有一些对象其实只需要一个,比如:windows Task Manager (任务管理器)、windows 回收站、线程池、缓存、对话框、处理偏好设置和注册表的对象、日志对象、充当打印机、显卡等设备的驱动程序对象。这些对象只能够拥有一个实例,如果创建出了多个实例,就会导致一些程序的问题。程序的行为异常,资源使用的过量,或者导致不一致的结果。常用来管理共享的资源,比如数据库的连接或者线程池。

3、优缺点:
优点:一个实例,减少内存开销;减少系统开销;避免对一个资源的多重占用;设置全局访问点,优化和贡献资源。

缺点:没有借口,扩展性差;不利于测试;与单一职责原则有冲突,单例模式把“要单例”和业务逻辑融合在一个类中。

4、实现
1、经典实现(线程不安全)

 1 class Singleton 
 2 { 
 3 public: 
 4 static Singleton* getInstance(); 
 5 protected: 
 6 Singleton(){} 
 7 private: 
 8 static Singleton *p; 
 9 };
10 
11 Singleton* Singleton::p = NULL; 
12 Singleton* Singleton::getInstance() 
13 { 
14 if (NULL == p) 
15 p = new Singleton(); 
16 return p; 
17 }
 1 1、当两个线程运行至if(instance==NULL)时,可能产生线程安全问题。
 2 
 3 改进:在用到的时候在初始化,这样程序效率比较高,但是有一个另外比较好的方法可以采用是提前初始化,将Instance设置为static之后直接初始化为Singleton对象,每次只需要执行返回操作即可。
 4 
 5 class Singleton {
 6 public:
 7     static Singleton* getInstance();
 8 
 9 protected:
10 
11     Singleton();
12 
13 private:
14     static Singleton *instance;
15 };
16 
17 //对单例直接进行初始化,而不再方法Instance中进行判断。
18 
19 Singleton* Singleton::instance = new Singleton;
20 
21 Singleton* Singleton::getInstance()
22 
23 {
24 
25   return instance;
26 
27 }
28 这样的话同样会导致问题,就是如果单例本来资源比较多,但是不需要创建那么早,就会消耗资源~
29 
30 2、另外一种提升因为同步导致的性能变差的方法称为“双重检验加锁”。方法如下:
31 
32 class Singleton {
33 public:
34     static Singleton* getInstance();
35 protected:
36     Singleton();
37 private:
38     static Singleton* instance;
39 }
40 
41 Singleton *Singleton::instance = NULL;
42 Singleton* Singleton::getInstance() {
43     //check 之前进行临界区加锁操作
44     //双重检验加锁
45     if(_instance == NULL ) {
46         lock();
47         if( instance == NULL) {
48             instance = new Singleton();
49         }
50         Unlock();
51     }
52     return _instance;
53 }
54 
55 思路是只有在第一次创建的时候进行加锁,当instance不为空的时候就不需要进行加锁的操作,这样就可以提升性能~

2、懒汉模式与饿汉模式
懒汉:故名思义,不到万不得已就不会去实例化类,也就是说在第一次用到类实例的时候才会去实例化,所以上边的经典方法被归为懒汉实现;

饿汉:饿了肯定要饥不择食。所以在单例类定义的时候就进行实例化。
特点与选择

区别:由于要进行线程同步,所以在访问量比较大,或者可能访问的线程比较多时,采用饿汉实现,可以实现更好的性能。这是以空间换时间。在访问量较小时,采用懒汉实现。这是以时间换空间。

 1 1.加锁实现线程安全的懒汉模式
 2 
 3 class Singleton 
 4 { 
 5   public: 
 6     static pthread_mutex_t mutex; 
 7     static Singleton* getInstance(); 
 8   protected: 
 9     Singleton() 
10     { 
11       pthread_mutex_init(&mutex); 
12     } 
13   private: 
14     static Singleton* p; 
15 }; 
16 
17 pthread_mutex_t Singleton::mutex; 
18 Singleton* Singleton::p = NULL; 
19 Singleton* Singleton::getInstance() 
20 { 
21   if (NULL == p) 
22   { 
23     pthread_mutex_lock(&mutex); 
24     if (NULL == p) 
25       p = new Singleton(); 
26     pthread_mutex_unlock(&mutex); 
27   } 
28   return p; 
29 }
30 
31 2.内部静态变量实现懒汉模式
32 
33 class Singleton 
34 { 
35   public: 
36   static pthread_mutex_t mutex; 
37   static Singleton* getInstance(); 
38   protected: 
39     Singleton() 
40     { 
41       pthread_mutex_init(&mutex); 
42     } 
43 }; 
44 
45 pthread_mutex_t Singleton::mutex; 
46 Singleton* Singleton::getInstance() 
47 { 
48   pthread_mutex_lock(&mutex); 
49   static singleton obj; 
50   pthread_mutex_unlock(&mutex); 
51   return &obj; 
52 }
 1 饿汉模式
 2 
 3 class Singleton 
 4 { 
 5   public: 
 6     static Singleton* getInstance(); 
 7   protected: 
 8     Singleton(){} 
 9   private: 
10     static Singleton* p; 
11 }; 
12 
13 Singleton* Singleton::p = new Singleton; 
14 Singleton* Singleton::getInstance() 
15 { 
16   return p; 
17 } 

 

posted @ 2019-05-19 22:34  unique_ptr  阅读(2258)  评论(0编辑  收藏  举报