设计模式之单例模式(Singleton)

实际中存在这样的场景:对于系统中的某些类来说,确保只有一个实例很重要,例如,一个系统只能有一个窗口管理器或文件系统,软件的全局配置信息应该只有一份。单例模式确保一个类只有一个实例,并提供一个全局访问点。全局变量虽然可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。这里介绍几种单例模式的C++实现:

懒汉模式

所谓懒汉模式就是第一次调用 getInstance() 时才产生对象,用到的时候才创建显得比较懒。

方法一: 使用局部静态变量,在C++0x之后才能保证 static 变量构造的线程安全性

class Singleton
{
public:
static Singleton& getInstance()
{
static Singleton instance;
return instance;
}

private:
Singleton() { }
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
};

注意:这种方法在C++0x之前仍然需要加锁保护。这里将默认构造函数、拷贝构造函数和 operator= 都设为 private,防止在类作用域外的构造和赋值;而且拷贝构造函数和 operator= 都不实现,使得即使在友元类中也无法进行拷贝构造和赋值。

方法二:使用静态成员变量(指针),注意使用互斥锁并且 double-check

class Singleton
{
public:
static Singleton *getInstance()
{
if (instance_ == NULL) {
MutexLockGuard lock(mutex_); // stack object of critical section

if (instance_ == NULL) {
instance_ = new Singleton();
}
}
return instance_;
}

private:
Singleton() { }
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);

MutexLock& mutex_; // RAII wrapper of mutex
static Singleton *instance_;
};

Singleton *Singleton::instance_ = NULL;

注意:在类的作用域之外不能再使用 static 修饰其成员。该方法使用 RAII 封装的互斥锁 MutexLock,并使用 MutexLockGuard 栈上对象封装临界区。double-check 应对多个线程同时通过第一个 if 的情况。程序结束的时候,操作系统应该能够回收 new 申请的内存,也可以再利用 RAII 回收这部分内存(在 private 域添加如下代码即可):

class GarbageCollector()
{
public:
~GarbageCollector()
{
if (Singleton::instance_) {
delete Singleton::instance_;
Singleton::instance_ == NULL;
}
}
}

static GarbageCollector garb;

饿汉模式

相对于懒汉模式,饿汉模式是在类加载的时候就完成了单例对象的创建,像人饿了看到东西先吃了再说。

class Singleton
{
public:
static Singleton *getInstance()
{ return instance_; }

private:
Singleton() { }
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);

static Singleton *instance_;
};

Singleton *Singleton::instance_ = new Singleton();

注意:饿汉模式创建单例对象时不存在多线程 race condition,所以不用加锁保护。同样可以使用上面描述的 GarbageCollector 类回收内存。

0%