C++设计模式之Singleton(单件/单例)模式

/ 0评 / 0

Published by orzz.org(). (http://orzz.org/cpp%e8%ae%be%e8%ae%a1%e6%a8%a1%e5%bc%8f%e4%b9%8bsingleton%e5%8d%95%e4%bb%b6%e5%8d%95%e4%be%8b%e6%a8%a1%e5%bc%8f/)

单例模式,顾名思义,此模式下的对象实例永远只有一个.很多初学者感觉这个模式的用处不大.但实际上,这个模式的应用非常广泛.

很多情况下,我们会很自然的使用单例的方式来实现功能,如全局内存池;全局资源管理器;某个全局的工具类工厂...在UI开发上,同一时间点上仅会出现一个实例的对话框,也可以使用单例实现.

单例的类图很简单:

它的目的,就是给程序提供一个全局唯一的访问点,用于访问某些资源;固定的算法或唯一的对象等.

单例模式的构建方式主要有两种:饿汉方式与懒汉方式.也就是静态初始化的单例与运行时根据需要初始化的单例.

当我们在实际应用中,很可能需要考虑线程安全问题.饿汉方式由于实现采用类成员静态初始化,始终是在主线程的主函数开始之前,以单线程方式进行的.所以当程序开启多个线程开始同步访问此单例类的时候,它总能返回唯一的单例.

然而懒汉方式则都是非线程安全的.就算是使用函数内部静态初始化的懒汉单例,由于目前的C++标准并没有规定编译器需要解决static的线程安全性,因此它也不是线程安全的.

也就是说,一个static声明的类变量,在多线程同时第一次访问时将有可能被构造多次(普通类型的变量,如bool,int的static是线程安全的.在C++0x中规定了static必须由编译器解决线程安全问题,因此支持C++0x的编译器编译的static类变量应该也不会有线程安全问题).

因此,我们可能需要这样来写线程安全的懒汉单例(注意,我这里使用的锁仅是语义上的"锁",在实际应用中需要使用对应平台提供的锁来完成相应操作):

在实际项目中,很多时候我们会用泛型的思想做单例模式,例如如下代码:

这是一个利用模板实现的饿汉单例,并且是线程安全的.其他方式实现的单例模式也可以类似推出对应的模板实现.

上面说了那么多,可以看出来,最方便的实现方式,应该是使用类成员静态初始化实现的饿汉方式单例了.但是这种单例模式有个最大的硬伤,就是在C++里默认的静态成员初始化顺序是不确定的.当一个程序中有多个单例,并且单例之间有相互的依赖时,就很可能出现因为构造顺序的不一致,导致当访问某些单例时出现返回的对象尚未初始化的问题.

解决的方法有很多种,在这里我一般是确定一个会被其他单例类依赖的单例,将它采用懒汉模式实现,而其他的单例则使用饿汉模式.此时的懒汉单例也不需要加锁,因为它必定会被其他的饿汉在构造的时候调用一次.


下面给出完整的示例代码:

Published by orzz.org(). (http://orzz.org/cpp%e8%ae%be%e8%ae%a1%e6%a8%a1%e5%bc%8f%e4%b9%8bsingleton%e5%8d%95%e4%bb%b6%e5%8d%95%e4%be%8b%e6%a8%a1%e5%bc%8f/)

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据