设计模式之 - 单例模式

来源:互联网 发布:赛维网络面试 编辑:程序博客网 时间:2024/05/29 18:15

一、引子

    单例模式是设计模式中使用很频繁的一种模式,在各种开源框架、应用系统中多有应用。

    几乎所有面向对象的程序中,总有一些类的对象需要是唯一的,例如,通过数据库句柄到数据库的连接是独占的。您希望在应用程序中共享数据库句柄,因为在保持连接打开或关闭时,它是一种开销。再如大家最经常用的IM,如QQ,在同一台电脑,一个帐号只能有唯一的登录。

1. 问题

怎样确保一个特殊类的实例是独一无二的(它是这个类的唯一实例),并且这个实例易于被访问呢? 

2. 解决方案

1)全局变量:一个全局变量使得一个对象可以被访问,但它不能防止你实例化多个对象。因为你的任何代码都能修改全局变量,这将不可避免的引起更多调试的意外。换句话说,全局变量的状态总是会出现一些问题的。

2)类构造函数私有和类自身的静态方法:让类自身负责保存它的唯一实例(静态变量)。这个类可以保证没有其他实例可以被创建(通过截取创建新对象的请求),并且它可以提供一个访问该实例的方法(静态方法)。这就是Singleton模式。

3. 适用性

在下面的情况下可以使用单件模式
1)当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。

2)当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。


二、定义与结构

    单例模式又叫做单态模式或者单件模式。在 GOF 书中给出的定义为:保证一个类仅有一个实例,并提供一个访问它的全局访问点。单例模式中的“单例”通常用来代表那些本质上具有唯一性的系统组件(或者叫做资源)。比如文件系统、资源管理器等等。

单例模式的目的就是要控制特定的类只产生一个对象,当然也允许在一定情况下灵活的改变对象的个数。那么怎么来实现单例模式呢?

    一个类的对象的产生是由类构造函数来完成的,如果想限制对象的产生,一个办法就是将构造函数变为私有的(至少是受保护的),使得外面的类不能通过引用来产生对象;同时为了保证类的可用性,就必须提供一个自己的对象以及访问这个对象的静态方法。   

三、实现

    单例的一般实现比较简单,由于构造函数是私有的,因此无法通过构造函数实例化,唯一的方法就是通过调用静态函数GetInstance。代码如下:

#include <iostream>using namespace std;class Singleton{public:static Singleton *GetInstance(){if (m_Instance == NULL ){m_Instance = new Singleton ();}return m_Instance;}static void DestoryInstance(){if (m_Instance != NULL ){delete m_Instance;m_Instance = NULL ;}}// This is just a operation exampleint GetTest(){return m_Test;}private:Singleton(){ m_Test = 10; }static Singleton *m_Instance;int m_Test;};Singleton *Singleton ::m_Instance = NULL;int main(int argc , char *argv []){Singleton *singletonObj = Singleton ::GetInstance();cout<<singletonObj->GetTest()<<endl;Singleton ::DestoryInstance();return 0;}
上面的代码在单线程的时候工作正常,但是多线程的情况下就有问题了。设想如果两个线程同时运行判断m_Instance是否为空,此时两个线程都会创建实例,不满足单例模式的要求。为此,可以在创建实例之前加上一个同步锁。

#include <iostream>using namespace std;void Lock(){}void UnLock(){}class Singleton{public:static Singleton *GetInstance(){if (m_Instance == NULL ){Lock(); // C++没有直接的Lock操作,请使用其它库的Lock,比如Boost,此处仅为了说明if (m_Instance == NULL ){m_Instance = new Singleton ();}UnLock(); // C++没有直接的Lock操作,请使用其它库的Lock,比如Boost,此处仅为了说明}return m_Instance;}static void DestoryInstance(){if (m_Instance != NULL ){delete m_Instance;m_Instance = NULL ;}}// This is just a operation exampleint GetTest(){return m_Test;}private:Singleton(){ m_Test = 10; }static Singleton *m_Instance;int m_Test;};Singleton *Singleton ::m_Instance = NULL;int main(int argc , char *argv []){Singleton *singletonObj = Singleton ::GetInstance();cout<<singletonObj->GetTest()<<endl;Singleton ::DestoryInstance();return 0;}

       上面的程序在加锁之前又进行了一次判断,如果实例已经存在就不需要再创建了。如果实例不存在,则先加锁,然后再判断实例是否存在,因为实例有可能在第一个if和加锁之前由另一个线程创建了。如果不存在实例,然后再创建。




0 0
原创粉丝点击