一.概述:
因为在设计或开发中,肯定会有这么一种情况,一个类只能有一个对象被创建,如果有多个对象的话,可能会导致状态的混乱和不一致。这种情况下,单例模式是最 恰当的解决办法。有很多地方需要这样的功能模块,如系统的日志输出,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 }
总结:单例模式适用于只允许一个实例存在的情况,它的实现必须满足三个条件,一是必须在类中就定义一个实例;二是必须有一个公有的静态方法来获取该实例;三是构造函数必须是私有的,来保证不容许别人通过调用构造函数来生成一个实例。在实现时要注意内存泄露问题,线程安全问题,性能问题。
本文出自 “水仙花” 博客,请务必保留此出处http://10704527.blog.51cto.com/10694527/1771034
原文地址:http://10704527.blog.51cto.com/10694527/1771034