# 游戏中的单例模式

“确保一个类只有一个实例，并为其提供一个全局访问入口。”

## 提供一个全局指针以访问唯一实例

下面是无法保证线程安全的

```cpp
class FileSystem 
{ 
public:
　static FileSystem& instance()
　{
  　//Lazy initialize.


　　if (instance_ == NULL) 
　　{
　　　 instance_ = new FileSystem();
　　}
　　return *instance_;
　}

private:
　FileSystem() {}
　static FileSystem* instance_;
};
```

更现代的版本，可以保证线程安全

```cpp
class FileSystem
{
public:
　static FileSystem& instance()
　{
　　static FileSystem *instance = new FileSystem();
　　return *instance;
　}

private:
　FileSystem() {}
};
```

* 如果我们不适用它，就不会创建实例
* 它在运行时初始化
* 你可以继承单例。这是一个强大但是经常被忽视的特性。

```cpp
class FileSystem
{
public:
　virtual ~FileSystem() {}
　virtual char* read(char* path) = 0;
　virtual void　write(char* path, char* text) = 0;
　};
```

为不同的平台定义派生类

```cpp
class PS3FileSystem : public FileSystem
{
public:
　virtual char* read(char* path)
　{ 
　　// Use Sony file IO API...
　}

　virtual void write(char* path, char* text)
　{
　　// Use sony file IO API...
　}
};

class WiiFileSystem : public FileSystem
{
public:
　virtual char* read(char* path)
　{ 
　　// Use Nintendo file IO API...
　}

　virtual void write(char* path, char* text)
　{
　　// Use Nintendo file IO API...
　}
};
```

将FileSystem变为一个单例

```cpp
class FileSystem
{
public: 
　static FileSystem& instance();

　virtual ~FileSystem() {}
　virtual char* read(char* path) = 0;
　virtual void　write(char* path, char* text) = 0;

protected: 
　FileSystem() {}
};

// 不同的平台构造不同的单例对象
FileSystem& FileSystem::instance()
{
#if PLATFORM == PLAYSTATION3
　static FileSystem *instance = new PS3FileSystem();
#elif PLATFORM == WII
　static FileSystem *instance = new WiiFileSystem();
#endif

　return *instance;
}
```

## 后悔使用单例的原因

* 它是一个全局变量，它们令代码晦涩难懂，假设我们正在跟踪其他人写的函数种的bug,如果这个函数没有使用全局状态，那么我们只需要将精力集中在理解函数体，和传递给它的参数就可以了。
* 全局变量促进了耦合
* 它对并发不友好
* 延迟初始化剥离了你的控制，游戏运行时在初始化某个单例对象，可能需要耗费较多的时间，我们不知道会发生什么，而在游戏开始时就初始化好比较好。

大部分的游戏都不依赖延迟初始化，像这样实现单例模式:

```cpp
class FileSystem
{
public:
　static FileSystem& instance() { return instance_; }

private:
　FileSystem() {}

　static FileSystem instance_;
};
```

比如日志对象，我们设计成了单例模式，慢慢的后面发现可能需要多个日志，后面成了火葬场。

## 将类限制为单一实例

当出现错误使用方式时，我们能够及时法线。

```cpp
class FileSystem
{
public:
　FileSystem()
　{
　　assert(!instantiated_);
　　instantiated_ = true;
　}
　~FileSystem() { instantiated_ = false; }

private:
　static bool instantiated_;
};

bool FileSystem::instantiated_ = false;
```

## 优先考虑的方式

* 传递进入：将需要的内容传进去，而不是用单例全局变量
* 在基类中获取它

```cpp
class GameObject
{
protected:
　Log& Log() { return log_; }

private:
　static Log& log_;
};

class Enemy : public GameObject
{
　void doSomething()
　{
　　getLog().write("I can log!");
　}
};
```

* 通过其他全局对象访问它

```cpp
class Game
{
public:
　static Game& instance() { return instance_; }

　Log&　　　　 log()　　　　 { return *log_; }
　FileSystem&　fileSystem()　{ return *files_; }
　AudioPlayer& audioPlayer() { return *audio_; }

　// Functions to set log_, et. al. ...



private:
　static Game instance_;
　Log　　　　 *log_;
　FileSystem　*files_;
　AudioPlayer *audio_;
};
```

这样起码比一大堆单例要好得多。

```cpp
Game::instance().getAudioPlayer().play(LOUD_BANG);
```
