C++中的单例模式

一、什么是单例模式

单例模式(Singleton Pattern,也称为单件模式),使用最广泛的设计模式之一。其意图是保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。

定义一个单例类,私有化它的构造函数,以防止外界创建单例类的对象;使用类的私有静态指针变量指向类的唯一实例,并用一个公有的静态方法获取该实例。

二、懒汉版(Lazy Singleton)

教学版,即懒汉版(Lazy Singleton):单例实例在第一次被使用时才进行初始化,这叫做延迟初始化。

// version 1.0
class Singleton
{
private:
	static Singleton* instance;
private:
	Singleton() {};
	~Singleton() {};
	Singleton(const Singleton&);
	Singleton& operator=(const Singleton&);
public:
	static Singleton* getInstance() {
		if(instance == NULL) {
			instance = new Singleton();
		}
		return instance;
	}
};

// init static member
Singleton* Singleton::instance = NULL;

Lazy Singleton存在内存泄露的问题,这里有两种解决方法:

  1. 使用智能指针
  2. 使用静态的嵌套类对象

对于第二种解决方法,代码如下:

// version 1.1
class Singleton
{
private:
	static Singleton* instance;
private:
	Singleton() { };
	~Singleton() { };
	Singleton(const Singleton&);
	Singleton& operator=(const Singleton&);
private:
	class Deletor {
	public:
		~Deletor() {
			if(Singleton::instance != NULL)
				delete Singleton::instance;
		}
	};
	static Deletor deletor;
public:
	static Singleton* getInstance() {
		if(instance == NULL) {
			instance = new Singleton();
		}
		return instance;
	}
};

// init static member
Singleton* Singleton::instance = NULL;

在程序运行结束时,系统会调用静态成员deletor的析构函数,该析构函数会删除单例的唯一实例。使用这种方法释放单例对象有以下特征:

  • 在单例类内部定义专有的嵌套类。
  • 在单例类内定义私有的专门用于释放的静态成员。
  • 利用程序在结束时析构全局变量的特性,选择最终的释放时机。

注意version 1.0与version 1.1都不是线程安全的,要使其线程安全,可以使用双检测锁模式(Double-Checked Locking Pattern):

static Singleton* getInstance() {
	if(instance == NULL) {
		Lock lock;  // 基于作用域的加锁,超出作用域,自动调用析构函数解锁
        if(instance == NULL) {
        	instance = new Singleton();
        }
	}
	return instance;
}

在单例类内再定义一个嵌套类,总是感觉很麻烦,所以《Effective C++》(Item 04)中的提出另一种更优雅的单例模式实现,使用函数内的 local static 对象。当第一次访问getInstance()方法时才创建实例:

// version 1.2
class Singleton
{
private:
	Singleton() { };
	~Singleton() { };
	Singleton(const Singleton&);
	Singleton& operator=(const Singleton&);
public:
	static Singleton& getInstance() {
		static Singleton instance;
		return instance;
	}
};

C++0X以后,要求编译器保证内部静态变量的线程安全性,故C++0x之后该实现是线程安全的,C++0x之前仍需加锁。

三、饿汉版(Eager Singleton)

饿汉版(Eager Singleton):指单例实例在程序运行时被立即执行初始化

// version 1.3
class Singleton
{
private:
	static Singleton instance;
private:
	Singleton();
	~Singleton();
	Singleton(const Singleton&);
	Singleton& operator=(const Singleton&);
public:
	static Singleton& getInstance() {
		return instance;
	}
}

// initialize defaultly
Singleton Singleton::instance;

由于在main函数之前初始化,所以没有线程安全的问题。但是潜在问题在于no-local static对象(函数外的static对象)在不同编译单元中的初始化顺序是未定义的。如果在初始化完成之前调用 getInstance() 方法会返回一个未定义的实例。

总结

  • Eager Singleton 虽然是线程安全的,但存在潜在问题;
  • Lazy Singleton通常需要加锁来保证线程安全,但局部静态变量版本在C++11后是线程安全的;
  • 局部静态变量版本(Meyers Singleton)最优雅,面试时建议写这个版本。

原文链接:http://songlee24.github.io/2014/03/11/singleton-pattern/

你可能感兴趣的:(C++中的单例模式)