实现类的单例模式的两种简单方式

来源:互联网 发布:行列式与矩阵运算法则 编辑:程序博客网 时间:2024/06/06 09:57

类的单例模式就是保证一个类仅有一个实例,并提供一个访问它的全局访问点。
说到设计一个类只能有一个实例,相信很多人第一反应都会想到类的静态成员,毕竟类的静态成员有着自己的特性,它是被所有类对象共享的,也就是他只有一份,而且类的静态成员变量是必须在类外进行初始化的。在了解了这两个特性之后(当然类的静态成员还有很多特性),再来看看怎么通过类的静态成员实现类的单例模式的饿汉模式和懒汉模式。
饿汉模式单例
首先来捋一下思路(废话有点多,可以直接看代码,看不懂再来看讲解),我们都知道实例化出一个类对象是要调用类的构造函数的,而且构造函数是系统自动调用的。为了防止系统调用构造函数构造类对象,只能把构造函数定义为私有的。这么做也就意味着在类外都无法直接调用构造函数创建对象了,而我们需要创建一个对象,此时,类的静态成员就派上用场了。定义一个私有的类指针类型的静态成员,它是有能力指向一个类对象的。在类外对该静态成员进行初始化的时候为其创建一个类对象(通过类的成员调用类的私有成员是可以的),该静态成员就指向一个类对象了,但是,这个静态成员是私有的,在类外没法使用,类对象也没法访问了。解决办法很简单,再给个public的静态成员函数,在函数内部将这个指针返回就好了。注意,类的静态成员是没有this指针的,不能通过this指针来访问。
接下来看代码:

class SingleClass{public:    static SingleClass* GetInstance()    {        return pInstance;    }private:    SingleClass()//构造函数    {        cout << "SingleClass()" << endl;    }    static SingleClass* pInstance;};//类外初始化类的静态成员变量,使其指向一个类对象SingleClass* SingleClass::pInstance = new SingleClass();

通过代码可以发现,此种方式在类加载时就完成了初始化,静态成员对象创建成功,也就是说,不管在主函数里边是否调用GetInstance函数,调用多少次,都会创建一个类对象。因此类的加载速度相对比较慢,但是获取对象的速度会很快。这是一种典型的以空间换时间的做法,它的缺点就是浪费空间,这也是饿汉模式的缺点。
来一步一步验证一下是不是这样呢?
这里写图片描述
已进入主函数就输出一句话告诉我们主函数开始运行,然后调用了类的静态成员函数。但是,运行结果却告诉我们,在进入主函数之前类类对象就已经创建好了。
再来验证一下是不是就算不调用类的静态成员函数GetInstance()也会创建类对象呢?
这里写图片描述
结果不言而喻。

懒汉模式单例
其实懒汉模式跟饿汉模式是很相似的,它的不同之处在于,该种实现方式并非在类的静态成员变量的初始化的时候创建对象,而是在静态成员函数里边创建。
直接看代码

class SingleClass{public:    static SingleClass* GetInstance()    {        if (pInstance == NULL)//保证第一次构造成功        {            pInstance = new SingleClass();        }        return pInstance;    }//static void DistoryInstance()//销毁函数,清理类对象//{//  if (NULL != pInstance)//  {//      delete pInstance;//      pInstance = NULL;//  }//}private:    static SingleClass* pInstance;//创建一个类类型指针的静态变量    SingleClass()//构造函数    {        cout << "SingleClass()" << endl;    }};SingleClass* SingleClass::pInstance = NULL;//类外初始化类的静态成员

从代码中可以看出,懒汉模式就是在第一次调用获取实例函数GetInstance()时创建一个类对象,然后让静态成员变量pInstance指向这个变量,之后再调用GetInstance()函数时,直接将指向类对象的指针pInstance返回,所以,无论调用多少次获取实例函数,返回的都是同一个对象,即返回的地址都是一样的。
这里写图片描述
懒汉模式在类的加载时不会创建对象,只有在调用GetInstance()函数时(第一次调用)才会创建对象,所以类的加载会很快,但是运行时获取对象的速度回稍微慢一点。但是该种实现方式有一个缺陷,那就是懒汉模式的线程是不安全的。当两个线程同时调用GetInstance()时可能就会导致并发问题。

最后简单说一下关于类对象的销毁问题,全局仅有一个实例,可能全局都在使用,它的生命周期就是从创建到程序结束,程序结束时自然而然地就销毁了,所以,一般情况下是不需要销毁函数的。

PS:欢迎各路大神提出宝贵意见~