您的位置:首页 > 编程语言 > C语言/C++

c++单例模式 ---超详细

2016-05-07 16:57 489 查看
一.概述:
因为在设计或开发中,肯定会有这么一种情况,一个类只能有一个对象被创建,如果有多个对象的话,可能会导致状态的混乱和不一致。这种情况下,单例模式是最
恰当的解决办法。有很多地方需要这样的功能模块,如系统的日志输出,GUI应用必须是单鼠标,MODEM的联接需要一条且只需要一条电话线,操作系统只能有一个窗口管理器,一台PC连一个键盘。单例模式有很多种实现方式,各自的特性不相同,使用的情形也不相同。今天要实现的是常用的三种,分别是饿汉式、懒汉式和多线程式。
《设计模式》一书中的实现有三个要素,定义一个单例类,要使用类的私有静态指针变量指向类的唯一实例(即在类中就生成一个对象),并用一个公有的静态方法获取该实例,并把构造函数定义为protected或private。

二.懒汉式实现单例模式:
懒汉式的特点是延迟加载,懒汉么,很懒,它只在要用到实例时才加载实例。

/****************************************
2     > File Name:lanhan.cpp
3     > Author:xiaoxiaohui
4     > mail:1924224891@qq.com
5     > Created Time:2016年05月07日 星期六 15时01分25秒
6 ****************************************/
7
8 #include<iostream>
9 using namespace std
10
11 class Singleton
12 {
13 private:
14     Singleton()
15     {}
16     static Singleton* _instace;   //静态的  私有的
17 public:
18     static Singleton* GetInstace()
19     {
20         if(_instance == NULL)
21         {
22             _instance = new Singleton();
23         }
24         return _instance;   //如果非空则new一个对象 后者返回原来的两个对象(所以保证了只有一个对象生成)
25     }
26
27 }
28
上面的这一实现存在内存泄露问题,因为没有释放_instance指针,下面为懒汉式的改进版:
8 #include<iostream>
9 using namespace std
10
11 class Singleton
12 {
13 private:
14     Singleton()
15     {}
16     static Singleton* _instance;   //静态的  私有的
17
18     class del
19     {
20     public:
21         ~del()
22         {
23             if(Singleton::_instance != NULL)
24             {
25                 delete Singleton::_instance;
26                 Singleton::_instance = NULL;
27             }
28         }
29     }
30     static del d;  //静态变量会在程序结束时调用它的析构函数
31 public:
32     static Singleton* GetInstance()
33     {
34         if(_instance == NULL)
35         {
36             _instance = new Singleton();
37         }
38         return _instance;   //如果非空则new一个对象 后者返回原来的两个对象(所以保证了只有一个对象生成)
39     }
40
41 }
该实现会在程序结束时调用静态变量的析构函数,从而delete了唯一的Singleton对象。
使用这种方法释放单例对象有以下特征:
1.在单例类内部定义专有的嵌套类。
2.在单例类内定义私有的专门用于释放的静态成员。
3.利用程序在结束时析构全局变量的特性,选择最终的释放时机。

但是现在还有问题,如果在多线程环境下,因为“if(_instance == NULL)”并不是原子的,会存在线程安全问题(如果一个线程刚刚判断了指针为空,这时另一个线程的优先级更高或者其它原因,打断了原来线程的执行,再次判断指针也会为空,所以会出现两个实例)下面为多线程环境下的懒汉式单例模式:
8 #include<iostream>
9 #include<stdlib.h>
10 #include<pthread.h>
11
12 pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
13 using namespace std
14
15 class Singleton
16 {
17 private:
18     Singleton()
19     {}
20     static Singleton* _instance;   //静态的  私有的
21
22     class del
23     {
24     public:
25         ~del()
26         {
27             if(Singleton::_instance != NULL)
28             {
29                 delete Singleton::_instance;
30                 Singleton::_instance = NULL;
31             }
32         }
33     }
34     static del d;  //静态变量会在程序结束时调用它的析构函数
35 public:
36     static Singleton* GetInstance()
37     {
38         pthread_mutex_lock(&lock);
39         if(_instance == NULL)
40         {
41             _instance = new Singleton();
42         }
43         pthread_mutex_unlock(&lock);
44         return _instance;   //如果非空则new一个对象 后者返回原来的两个对象(所以保证了只有一个对象生成)
45     }
46
47 }
48
但现在还有问题,当有大量的线程时,只会有一个线程进入互斥锁,然后执行下面的代码而其它线程只能等待,并且加锁是一个繁重的过程,这样会导致加很多次锁,这样就太不高效了。下面是高效版的多线程环境下的懒汉式单例模式:
8 #include<iostream>
9 #include<stdlib.h>
10 #include<pthread.h>
11
12 pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
13 using namespace std
14
15 class Singleton
16 {
17 private:
18     Singleton()
19     {}
20     static Singleton* _instance;   //静态的  私有的
21
22     class del
23     {
24     public:
25         ~del()
26         {
27             if(Singleton::_instance != NULL)
28             {
29                 delete Singleton::_instance;
30                 Singleton::_instance = NULL;
31             }
32         }
33     }
34     static del d;  //静态变量会在程序结束时调用它的析构函数
35 public:
36     static Singleton* GetInstance()
37     {
38         if(_instance == NULL)
39         {
40              pthread_mutex_lock(&lock);
41              if(_instance == NULL)
42              {
43                 _instance = new Singleton();
44              }
45              pthread_mutex_unlock(&lock);
46         }
47         return _instance;   //如果非空则new一个对象 后者返回原来的两个对象(所以保证了只有一个对象生成)
48     }
49
50 }
这样只有没有Singleton实例时才会进入加锁的代码,而当有Singleton实例时不需要进入加锁的代码中,直接返回已存在的实例就行了。

三.饿汉式的单例模式:在一开始就创建实例,要用时直接返回即可。饿汗式的单例模式没有线程安全问题,因为所以线程都只能访问一个已存在的对象,无论线程怎么调度都不会有多个对象出现。因为对象是一个静态变量(不是指针),会在程序结束时自动调用它的析构函数,所以不用考虑内存泄露问题。
饿汗式的特点:代码简单,不会出现内存泄露,是线程安全的。

1 /****************************************
2     > File Name:erhan.cpp
3     > Author:xiaoxiaohui
4     > mail:1924224891@qq.com
5     > Created Time:2016年05月07日 星期六 16时10分56秒
6 ****************************************/
7
8 #include<iostream>
9 using namespace std
10
11
12 class Singleton
13 {
14 private:
15     Singleton()
16     {}
17     static Singleton instance ;    //静态变量只会有一份数据存在 从而保证只有一个实例
18 public:
19     static Singleton& GetInstance()
20     {
21         return instance;
22     }
23 }
声明一个局部的静态变量,而静态变量在全局范围内只有一份数据,所以无论调用多少此GetInstance,返回的都是那一个实例。
但这个实现存在问题,Singleton singleton = Singleton :: GetInstance(),这么做就出现了一个类拷贝的问题,这就违背了单例的特性。产生这个问题原因在于:因为在这里没有实现拷贝构造函数,编译器会为类生成一个默认的拷贝构造函数,来支持类的拷贝。
解决方法:1.自己再定义一个拷贝构造函数和operator=,这个拷贝构造函数和operator=什么都不做。
2.返回一个Singleton指针。
下面为方法2的代码:
8 #include<iostream>
9 using namespace std
10
11
12 class Singleton
13 {
14 private:
15     Singleton()
16     {}
17     static Singleton instance ;    //静态变量只会有一份数据存在 从而保证只有一个实例
18 public:
19     static Singleton* GetInstance()
20     {
21         return &instance;
22     }
23 }


总结:单例模式适用于只允许一个实例存在的情况,它的实现必须满足三个条件,一是必须在类中就定义一个实例;二是必须有一个公有的静态方法来获取该实例;三是构造函数必须是私有的,来保证不容许别人通过调用构造函数来生成一个实例。在实现时要注意内存泄露问题,线程安全问题,性能问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c++ 单例模式 超详细