单例是使用静态变量使得程序中只有一个实体对象的抽象方法,是一种共享类全局对象的方法。优点是:确保一个类只创建一个实例;为对象分配和销毁提供控制;支持线程安全地访问对象的全局状态;避免污染全局命名空间
1 单例实现
上述中的单例通过静态成员变量和静态成员函数实现,静态成员函数getInstance()会对静态成员instance进行实例化,并通过此进行函数接口的调用。
上述实现中优点是通过全局函数getInstance()来访问全局静态成员变量,进而能使用其方法,但调用时“Singleton::getInstance().display()”代码长度过长,可以把display函数声明为静态方法,内部调用instance的私有方法_display减少代码的书写,即
这样做的好处是可以声明外部接口为共有的静态成员函数,其他具体实现接口都是私有的,避免了错误访问(且getInstance()方法也应该设置成私有,防止单例对象赋值和拷贝)。
2 单例使用
比较适合使用单例的情况有:日志类,配置类,共享资源类等。这些例子有以下特性:
Ø 如果我们不使用它,就不会创建实例。即getInstance()是Lazy Initial的,单例只在第一次使用时进行初始化,如果不使用则不进行初始化。
Ø 它在运行时初始化,即单例也可以在其初始化时引用另一个单例。
Ø 单例可以继承(见代码链接)。
3 扩展
3.1 线程安全的单例
前面的getInstance()实现不是线程安全,因为单例的静态初始化存在竞态条件,如果恰好有两个线程同时调用该方法,那么示例可能被构造两次,或者在一个线程完成初始化示例前另外一个线程就调用了该实例。解决方法是在竞态条件的代码前后添加互斥锁实现线程安全。
static Mutex mutex
Singleton &Singleton::getInstance()
{
ScopedLock lock(&mutex); //在函数退出时释放互斥锁
static Singleton instance;
return instance;
}
上述解决方案的潜在问题是开销较大,因为每次调用时都要请求加锁。另外可用双重检查锁定模式(double check locking pattern)
Singleton &Singleton::getInstance()
{
static Singleton *instance = NULL;
if(instance == NULL) //检查#1
{
Mutex mutex;
ScopedLock lock(&mutex);
if(instance == NULL) //检查#2
{
instance = new Singleton();
}
}
return *instance;
}
(补充:C++11保证一个局部静态变量的初始化只进行一次,哪怕是在多线程的情况下也是如此)
参考文献:
[1]http://www.oodesign.com/singleton-pattern.html
[2]《C++ API设计》
[3]《游戏编程模式》