为什么需要单例模式

       在很多项目中,我们可能都会遇到这样一种情况:某个类的对象在整个项目是唯一的,它不能也没必要被实例化多次,比如窗口管理器、皮肤加载器等等。这就催生出了如下的现实需求:如何确保某个类只有一个实例。

       在结构化程序设计方法中,我们可以使用全局变量来实现唯一实例,但它不能保证唯一性,因为它无法确保使用者不在其他的地方进行实例化。在面向对象程序设计方法中,我们有了更好的选择;我们可以通过将类的构造函数隐藏起来,以防止用户多次实例化对象,同时给用户提供一个获取该类实例的接口。这样就从类本身保证了对象的唯一性,防止了用户的误用。

什么是单例模式

       单例模式,又称单件模式,是指在整个模块中,一个类只有一个实例。单例模式有“饿汉”式和“懒汉”式两种:所谓“饿汉”式,是指不管用户有没有使用该类的实例,它都会创建一个实例;所谓“懒汉”式,是指只有当用户初次使用该类的实例时,才会创建一个实例,而以后需要再次使用该类的实例时,直接返回初次创建的那个实例即可。在C++中,我们通常通过类的静态成员变量来实现单例模式。下面给出单例模式最原始的实现版本,暂且美其名曰原生态实现。

单例模式之原生态实现

      Singleton1.h文件

 

Singleton1.cpp文件

上述代码很容易看懂。我们在静态成员函数Singleton中对静态成员变量m_pSingleton进行了判断,若为NULL,则创建一个对象;否则直接返回已创建的对象。另外,我们将构造函数和析构函数都声明成了protected类型的,这样就消除了用户在外部实例化对象的可能,保证了对象的唯一性。最后,由于我们是在堆上动态创建对象的,因此提供了一个静态成员函数Close用于最终释放这个动态创建的对象。

       仔细研究上述代码后,可以发现,这种实现方案中有一个静态成员函数Close,但却并没有提供对应的Open函数,单从接口对称方面来说,似乎并不美观。于是,我们便有了下面的单例模式之美学实现方案。

单例模式之美学实现

      Singleton2.h文件

   

Singleton2.cpp文件

在上述方案中,我们在静态成员函数Open中了进行了对象的创建工作,而静态成员函数Singleton完成的工作则相对简单,直接返回静态成员变量m_pSingleton即可。现在我们可以来看一下,如何使用该单例呢?需要先Open,然后调用Singleton,最后调用Close。很好,但这样是不是就完美了呢?对于追求极致的人来说,远非完美。如果我们可以不调用OpenClose,而只需直接调用Singleton,岂不是更好。这样既可以方便使用者,又可以避免误用和内存泄漏。这就需要我们自己来负责释放已经创建的唯一实例m_pSingleton。于是,我们又有了下面的单例模式之精简实现方案。

单例模式之精简实现

      Singleton3.h文件

   

    Singleton3.cpp文件

   

该方案中的静态成员函数Singleton的实现与原生态方案中Singleton的实现完全一样,重点在于实现文件中声明的全局结构体变量term。在该结构体的析构函数中,我们做了原本需要在Close中完成的工作:释放已经创建的唯一实例。在该方案中值得注意的一点是,我们将析构函数变成了public的,这是由于我们需要在单例类外部释放已经创建的唯一实例,并且不想将结构体TSingleton3_Term暴露给用户。

现在好了,直接使用Singleton即可,不用Open,也不用Close,但该方案还存在一点瑕疵,就是没有考虑到默认的拷贝构造函数和赋值运算符。分析下面的代码,我们可以通过拷贝构造函数再创建一个对象,这显然破坏了单例模式的设计初衷。于是,单例模式之加强版实现应运而生。 

单例模式之加强版实现

Singleton4.h文件

Singleton4.cpp文件

可以看到,该实现方案与精简实现方案几乎一样,只不过它将拷贝构造函数和赋值运算符隐藏了起来,以防止用户在外部构造对象。至此,我们的单例类似乎已经很完善了,但如果我们设身处地地为用户想一下,可以发现,每次设计一个单例类时,都需要编写那些几乎一模一样的OpenSingletonClose函数,这对于那些懒惰的软件开发人员来说,简直是不可忍受的。幸好,我们还有泛型编程,于是,我们有了如下的单例模式之模板实现(仅给出模板精简版实现)。

单例模式之模板实现

      Singleton5.h文件

  

懂得泛型编程的人应当不难理解上述代码,所以这里就不再赘述了。那么,如何使用这个模板类呢?只需将我们需要设置成单例模式的类从其派生即可。下面给出一个派生类的具体实现。

DerivedSingleton5.h文件

DerivedSingleton5.cpp文件

在该派生类中,我们将模板类作为其友元类,这是因为在模板类的Singleton函数中,需要调用派生类的构造函数。

总结

       以上一共给出了单例模式的5种实现方案,给出这些方案的主要目的并不在于比较哪个方案更好,而是展现一种思考问题的方式和思路。懂得如何思考一个问题,可能比解决一个问题更有价值,更有助于我们成长。

作者: 胡健 发表于 2010-12-15 12:05 原文链接

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架
新浪微博粉丝精灵,刷粉丝、刷评论、刷转发、企业商家微博营销必备工具"