描述:

保证一个类只有一个实例,并且提供了访问该实例的全局访问点.

实现方案

私有构造器保证它是唯一的,公开的静态方法instance()提供了访问实例的接口.

在首次被请求时,instance()负责惰性实例化该单例.
多线程情况下,C++11标准保证了本地(局部)静态变量只会初始化一次,因此是线程安全的.

class Singleton
{
public:
  static Singleton& instance()
  {
    static Singleton *instance = new Singleton();
    return *instance;
  }
private:
  Singleton() {}
};复制代码

给实例提供更方便的访问方法

便利的访问是使用单例的一个主要原因,可以在不同的地方访问实例,代价就是不想要对象的地方,也能轻易使用.

除了单例,代码中获取实例的几种方式

  1. 作为函数参数传进来

渲染对象的函数,需要传入一个表示图形设备的对象,管理渲染状态,将其传给渲染函数是自然地..

相反,AI函数虽然也需要写日志,但是日志不是它关注的核心,Log作为参数传进来就很奇怪.
2) 从基类中获得

很多游戏有浅层但是宽泛的继承层次,比如,GameObject基类,每个游戏中的对象都继承它,很大一部分代码存在于这些派生出来的子类中,意味着这些类有对同样事务的相同获取方法.

class GameObject
{
// 使用protect让派生对象使用实例被限制在子类的沙盒中
protected:
  Log& getLog() { return log_; }
private:
  static Log& log_;
};
class Enemy : public GameObject
{
  void doSomething()
  {
    getLog().write("I can log!");
  }
};复制代码
  1. 已经是全局的对象中获取

我们可以让现有的全局对象捎带需要的对象,来减少全局对象的数目.

这样只要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_;
};复制代码
  1. 从服务器定位器中获得

定义一个类,目标就是为对象提供全局访问,这种模式被成为服务器定位器模式.