单例模式几种实现方式
单例模式作为设计模式中最简单,应用最广泛的一个,熟练掌握该设计模式无论对于面试还是项目使用,想来是极好的。最近也是看面试题的时候看到了这块,发现自己忘的都差不多了,又重新熟悉了下,并且综合网上各种混杂的实现方式以及四人帮的《设计模式》,把几种单例模式的关系捋清,并且给出自己的解答
总体呢,单例模式有两种,一种是懒汉,一种是饿汉,这两种是实现单例模式的不同思路。第一种呢,就是“懒”嘛,只有具体使用这个实例的时候才会去实例化。那饿汉则反之,程序一开始就实例化。
那明显呢,上述两者有自己的优缺点。懒汉的优点就是实例创建延迟至真正使用的时候,无疑对内存效率使用较高。但是缺点是不具有线程安全性,也就是说不做一些处理就不能适应多线程;饿汉的优点则是具有线程安全性,因为在程序运行一开始即在主线程已经实例化好了。但缺点则是内存效率较低。简单来说,懒汉是以时间换空间;饿汉是以空间换时间。
最简单的懒汉实现
|
|
该实现方式是最简单的单例模式,但带来的问题是 不具有线程安全性,当多线程环境下,会实例化多个对象。所以就引申出下一个多线程模式。
懒汉多线程加锁实现
|
|
我们采用加锁的方式,因为一个时刻只有一个线程能得到锁,所以可以解决多线程问题。但是仔细分析又有一个问题,那就是每次去getInstance()时都会去加上一个锁,而加锁是一个很耗时的操作,所以在没必要的时候要避免去加锁。这就是下一个改进版的double-check。
懒汉多线程加锁改进实现(double-check)
|
|
这种写法提高了效率,但是实现起来比较复杂而且容易出错。下来推荐更加优秀的写法。
懒汉最简洁实现(局部静态变量)
|
|
最简洁,没有之一。c++11标准可以保证局部静态变量的线程安全性,所以这种方式不仅简洁,而且不用考虑其他实现方式还要考虑的delete过程,所以非常推荐这种方法,不过这个也有一个不适用的地方,对于打印机,加锁的文件等并不是在不用的时候就delete掉,而是必须在程序结束时delete 的场景,这个实现方式就无法解决了。而饿汉的一种改进形式可以完美解决这个问题(通过内嵌一个static的类)。
饿汉实现
|
|
由于在程序开始时即以静态初始化的方式 主线程完成了初始化,所以可以保证线程安全性。在性能要求比较高时,采用这种方式,可以避免频繁的加锁和解锁造成的资源浪费。
饿汉自动销毁实现
|
|
在程序运行结束后,系统会对静态存储区的变量释放,这就包括全局变量以及类的静态成员变量(这里是同等对待的),我们定义了Singleton私有的类GC来实现最终释放我们的实例。释放静态变量gc时,调用其析构函数~GC(),在这里实现了对m_Instance的释放。
总结
以上几种单例模式的实现都有其特点,根据实际场景可能会有所结合或者改动。目前来说c++的所有单例模式实现原理思路就是上述几种啦。其实还有一种更吊的方式,既能按需创建实例(即懒汉),又能自动释放,又很简洁。那就是c#的静态构造函数。具体实现机制自行百度。
转载请注明出处