描述:
保证一个类只有一个实例,并且提供了访问该实例的全局访问点.
实现方案
私有构造器保证它是唯一的,公开的静态方法instance()提供了访问实例的接口.
在首次被请求时,instance()负责惰性实例化该单例.
多线程情况下,C++11标准保证了本地(局部)静态变量只会初始化一次,因此是线程安全的.
class Singleton
{
public:
static Singleton& instance()
{
static Singleton *instance = new Singleton();
return *instance;
}
private:
Singleton() {}
};
给实例提供更方便的访问方法
便利的访问是使用单例的一个主要原因,可以在不同的地方访问实例,代价就是不想要对象的地方,也能轻易使用.
除了单例,代码中获取实例的几种方式
- 作为函数参数传进来
渲染对象的函数,需要传入一个表示图形设备的对象,管理渲染状态,将其传给渲染函数是自然地.. 相反,AI函数虽然也需要写日志,但是日志不是它关注的核心,Log作为参数传进来就很奇怪.
- 从基类中获得
很多游戏有浅层但是宽泛的继承层次,比如,GameObject基类,每个游戏中的对象都继承它,很大一部分代码存在于这些派生出来的子类中,意味着这些类有对同样事务的相同获取方法.
class GameObject
{
// 使用protect让派生对象使用实例被限制在子类的沙盒中
protected:
Log& getLog() { return log_; }
private:
static Log& log_;
};
class Enemy : public GameObject
{
void doSomething()
{
getLog().write("I can log!");
}
};
- 已经是全局的对象中获取
我们可以让现有的全局对象捎带需要的对象,来减少全局对象的数目.
这样只要Game是全局可见的,就可以通过接口访问其他系统.
class Game
{
public:
static Game& instance() { return instance_; }
// 设置log_, et. al. ……
Log& getLog() { return *log_; }
FileSystem& getFileSystem() { return *fileSystem_; }
AudioPlayer& getAudioPlayer() { return *audioPlayer_; }
private:
static Game instance_;
Log *log_;
FileSystem *fileSystem_;
AudioPlayer *audioPlayer_;
};
- 从服务器定位器中获得
定义一个类,目标就是为对象提供全局访问,这种模式被成为服务器定位器模式.