C++设计模式之单例模式

来源:互联网 发布:极品飞车ol mac 编辑:程序博客网 时间:2024/05/21 19:35

 神州上下五千年,为了抵御匈奴的入侵,无数的人民投入到兴建万里长城浩瀚的工程中。也正因为有这些劳动人民辛勤的付出,才有了今天世界八大奇迹之一-----独一无二的万里长城。在软件开发中,同样存在这种唯一的实例,它就是接下来要讲的单例模式。

1、单例模式:

单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种对象创建型模式。

      单例模式有三个要点:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例



       Singleton(单例):在单例类的内部实现只生成一个实例,同时它提供一个静态的getInstance()工厂方法(全局唯一访问这个类的访问点),让客户可以访问它的唯一实例;为了防止在外部对其实例化,将其构造函数设计为私有;在单例类内部定义了一个Singleton类型的静态对象,作为外部共享的唯一实例
        (1) 由于每次使用new来实例化Singleton类,都将产生一个新的对象。为了确保Singleton具有唯一的实例,应该禁止使用new产生一个实例。解决办法就是将Singleton类的构造函数设置为私有的。
[cpp] view plaincopy
  1. private:  
  2.     Singleton()  
  3.     {  
  4.   
  5.     }  
        (2)  将构造函数改为private修饰后该如何创建对象呢?不要着急,虽然类的外部无法再使用new来创建对象,但是在Singleton的内部还是可以创建的,可见性只对类外有效。因此,我们可以在Singleton中创建并保存这个唯一实例。为了让外界可以访问这个唯一实例,需要在Singleton中定义一个静态的Singleton类型的私有成员变量,如下代码所示:
[cpp] view plaincopy
  1. private:  
  2.     static Singleton * m_pSingleton;  
        (3)Singleton 类型的m_pSingleton对象的可见性设置为private,既不能通过new产生一个实例对象,那如何访问Singleton类m_pSingleton,并且给它赋值呢?答案是增加一个公有的静态方法,代码如下:
[cpp] view plaincopy
  1. public:  
  2.     static Singleton * GetInstance()  
  3.     {  
  4.         if( NULL == m_pSingleton  )  
  5.         {   
  6.             m_pSingleton  = new Singleton();    
  7.         }  
  8.           
  9.         return m_pSingleton  ;  
  10.          }  
              m_pSingleton  = new Singleton() 将调用Singleton类的私有构造函数
        完整的单例模式代码如下:
[cpp] view plaincopy
  1. class Singleton  
  2. {  
  3. private:  
  4.     static Singleton * m_pSingleton;  
  5.     Singleton()  
  6.     {  
  7.   
  8.     }  
  9. public:  
  10.     static Singleton * GetInstance()  
  11.     {  
  12.         if( NULL == m_pSingleton  )  
  13.         {   
  14.             m_pSingleton  = new Singleton();    
  15.         }  
  16.           
  17.         return m_pSingleton  ;  
  18.         }   
  19. };  


2、线程池的设计与实现

        在一个线程池中,存放着多个互不相同的线程,每个线程都只能有一个实例。例如:暴风影音这个线程,只能有一个实例(也就是一次不可能同时开启两个暴风影音)。请设计一个线程池,要求能够往线程池中添加各种线程、移除某个线程、获取某个线程,枚举所有线程。
        头文件代码如下:
[cpp] view plaincopy
  1. #ifndef _THREAD_POOL_H_  
  2. #define _THREAD_POOL_H_  
  3.   
  4. #include <iostream>  
  5. #include <vector>  
  6. #include <string>  
  7.   
  8. using namespace std;  
  9.   
  10.   
  11. //线程池类  
  12. class ThreadPool  
  13. {  
  14. private:  
  15.     //线程池单例对象  
  16.     static ThreadPool * m_pThreadPool;  
  17.     //存放各个线程  
  18.     vector<string> m_Vector;        
  19. private:  
  20.     //私有构造函数  
  21.     ThreadPool()  
  22.     {  
  23.   
  24.     }  
  25.   
  26. public:  
  27.     //返回唯一的线程池对象  
  28.     static ThreadPool * GetInstance();  
  29.     //添加线程  
  30.     void AddThread(string strThreadName);  
  31.     //移除线程  
  32.     void RemoveThread(int nThreadIndex);  
  33.     //获取线程  
  34.     string GetThread(int nThreadIndex);  
  35.     //枚举线程  
  36.     void DisplayThread();  
  37. };  
  38.   
  39.   
  40. #endif  
    cpp实现代码如下:
[cpp] view plaincopy
  1. #include <iostream>  
  2. #include "ThreadPool.h"  
  3.   
  4. using namespace std;  
  5.   
  6. //单例对象初始化为空  
  7. ThreadPool * ThreadPool::m_pThreadPool = NULL;  
  8.   
  9. //返回唯一的线程池对象  
  10. ThreadPool * ThreadPool::GetInstance()  
  11. {  
  12.     if( NULL == m_pThreadPool )  
  13.     {  
  14.         m_pThreadPool = new ThreadPool();  
  15.     }  
  16.   
  17.     return m_pThreadPool;  
  18. }  
  19.   
  20.   
  21.   
  22. //添加线程  
  23. void ThreadPool::AddThread(string strThreadName)  
  24. {  
  25.     m_Vector.push_back(strThreadName);  
  26. }  
  27.   
  28.   
  29.   
  30. //移除线程  
  31. void ThreadPool::RemoveThread(int nThreadIndex)  
  32. {  
  33.     int nVectorSize = m_Vector.size();  
  34.     vector<string>::iterator ite = m_Vector.begin();  
  35.   
  36.     if( 0 <= nThreadIndex  && nThreadIndex < nVectorSize )  
  37.     {  
  38.         ite = ite + nThreadIndex;  
  39.   
  40.         m_Vector.erase(ite);  
  41.     }  
  42.       
  43. }  
  44.   
  45.   
  46.   
  47. //获取线程  
  48. string ThreadPool::GetThread(int nThreadIndex)  
  49. {  
  50.     int nVectorSize = m_Vector.size();  
  51.     vector<string>::iterator ite = m_Vector.begin();  
  52.   
  53.     if( 0 <= nThreadIndex  && nThreadIndex < nVectorSize )  
  54.     {  
  55.         return m_Vector[nThreadIndex];  
  56.     }  
  57. }  
  58.   
  59.   
  60.   
  61. //枚举线程  
  62. void ThreadPool::DisplayThread()  
  63. {  
  64.     int nVectorSize = m_Vector.size();  
  65.   
  66.     forint nIndex = 0; nIndex < nVectorSize; nIndex++ )  
  67.     {  
  68.         cout << m_Vector[nIndex] << endl;  
  69.     }  
  70. }  
        测试代码如下:
[cpp] view plaincopy
  1. #include <iostream>  
  2. #include "ThreadPool.h"  
  3. using namespace std;  
  4.   
  5. int main()  
  6. {  
  7.     ThreadPool * pThreadPool1 = ThreadPool::GetInstance();  
  8.     ThreadPool * pThreadPool2 = ThreadPool::GetInstance();  
  9.   
  10.     if( pThreadPool1 == pThreadPool2 )  
  11.     {  
  12.         cout << "线程池1和线程池2相等" << endl;  
  13.   
  14.         pThreadPool1->AddThread("听听动听");  
  15.         pThreadPool1->AddThread("暴风影音");  
  16.         pThreadPool1->AddThread("迅雷");  
  17.           
  18.         cout << endl;       
  19.         cout << "线程池里面的线程列表:" << endl;  
  20.         pThreadPool1->DisplayThread();  
  21.     }  
  22.     else  
  23.     {  
  24.         cout << "创建了2个线程池" << endl;  
  25.     }  
  26.   
  27.     delete pThreadPool1;  
  28.     pThreadPool1 = NULL;  
  29.   
  30.     return 0;  
  31. }  
         编译并运行程序,输出结果如下:
 

3、单例模式总结

1.主要优点

        单例模式的主要优点如下:

        (1) 单例模式提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它。

        (2) 由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。


2.主要缺点

        单例模式的主要缺点如下:

        (1) 由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。

        (2) 单例类的职责过重,在一定程度上违背了“单一职责原则”。因为单例类既充当了工厂角色,提供了工厂方法,同时又充当了产品角色,包含一些业务方法,将产品的创建和产品的本身的功能融合到一起


3、适用场景

         系统只需要一个实例对象或者需要考虑资源消耗太大而只允许创建一个对象。通常情况下,单例模式用得不多,如果你的应用程序大量的使用了单例模式,那么可能需要好好检查你的设计。

    (1)线程池管理器:线程池中存在多个互不相同的线程,能够往线程池中添加线程,移除线程,枚举线程等。这个线程池管理器就是一个单例对象。

    (2)插件管理器: 通常在带有菜单栏的工程项目中,为了扩展的需要,把一些业务逻辑以插件的方式提供。也就是说软件运行过程中会检查某个目录下是否存在所需的插件(一般是动态库),如果有就把它动态添加到菜单栏中。例如:PDF阅读器旧的版本没有百度搜索功能,新版本的PDF阅读器则以插件的方式提供了百度搜索功能。这个插件管理器就是一个单例对象。

    (3)Windows任务管理器: Widnows任务管理器对各个进程进行管理。不论你按下多少次CTRL+ALT+DEL(任务管理器快捷键),都只会弹出一个任务管理器对话框。也就是说任务管理器就是一个单例对象。

    (4)服务器负载均衡器: 能够添加服务器,移除某个服务器,对客户端的链接请求,随即选择一个服务器进行分发处理。这个服务器负载均衡器也是一个单例对象。

    (5)注册表管理器:提供对各软件的配置,初始化各软件。系统唯一存在一个注册表。

    (6)系统序列号:现在很多软件都有进行加密处理,在使用过程中需要输入序列号(该序列号可能是电脑机器码)。这些序列号各不相同。 

     (7)  Windows系统回收站机制: 系统运行到关机,回收站都存在。且系统只有一个回收站,用来管理被删除的资源。

     (8)  操作系统的文件系统:  一个操作系统只能存在一种文件系统,如Windows默认的NTFS文件系统。

     (9) 网站计数器:  现在很多的论坛,网站都会显示当前在线的人数。这个在线计数器就是单例模式的一种应用,整个软件只有一个在线计数器类,供各个用户共享。用户登陆,计数器加1,用户退出,计数器减1。

     (10)  应用程序的日志应用: 一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加.

     (11)  数据库连接池的设计一般也是采用单例模式: 因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗

     (12) MFC的全局应用实例theapp:  整个MFC程序就只有一个app对象。

     (13) 缓存机制: 通过把系统常用的资源放到缓存中,Cpu直接从缓存中读取资源进行处理,节省时间。这个缓存一般情况下系统就只有一个。比如:cache

     (14) 驱动: Windows的声卡、显卡、网卡、打印机驱动等。唯一的存在一种驱动。

     (15) 配置文件管理: 通常把一些配置信息写入到xml配置文件中,软件运行过程中只需要唯一一个配置文件对象既可实现读取配置文件的信息。当有多个功能需要读取配置文件中的信息时,也可以通过这个唯一的配置文件对象来读取信息。这个对象被共享,它是一个单例对象。

             (16)生活中的单例: 世界上只有一个中国、也只有一个福建; 世界上没有完成相同的叶子;每个人的DNA,指纹信息都是独一无二的; 某金店打造的全球仅有的一颗钻戒等等。



http://blog.csdn.net/apelife/article/details/38374321

0 0
原创粉丝点击