プログラムをしていると何処からでもアクセスできる情報や機能が欲しいことが多々あります。
そんな時に役に立つのがこのシングルトン(Singleton)です!
シングルトン(Singleton)
シングルトンとはGof(Gang of Four)が考案したデザインパターンの一つです。
※Gof(Gang of Four)ってなんか厨二っぽくていいですよねw
これはプログラム中にクラスのインスタンスが1つであることを保証する手法のことです。
例えば敵を管理するとき、敵の管理処理が複数個所に散らばっているとそれぞれ更新する必要が出てくるので非効率だったりします。
そんなときに敵を管理するクラスをシングルトンにしておくと何処からでもアクセスできて管理も楽になります。
2つ以上作成できないことも保証されるので安全性もあります。
ただ実際には使う人次第で安全にも危険にもなるのでただ楽だから使っているとハマることもありますが…。
では早速実装の方法を紹介していきます!
まずは単純な実装についてです。
- class Singleton
- {
- private:
- // コンストラクタの外部での使用を禁止
- Singleton(void)
- {}
- ~Singleton(void)
- {}
- private:
- // コピーコンストラクタの使用を禁止
- Singleton(const Singleton&);
- Singleton& operator = (const Singleton&);
- public:
- static void Build(void)
- {
- assert(!m_pInstance);
- m_pInstance = new Singleton;
- }
- static void Release(void)
- {
- assert(m_pInstance);
- delete m_pInstance;
- m_pInstance = NULL;
- }
- static Singleton& Instance(void)
- {
- return *m_pInstance;
- }
- private:
- static Singleton* m_pInstance;
- };
- Singleton* Singleton::m_pInstance = NULL;
こんな感じになります。
やっていることは簡単です。
- コンストラクタとデストラクタをprivateにすることで外部で作成・削除することを禁止
- コピーコンストラクタとoperatorを定義だけで実装しないことによりコピーの禁止
- staticで定義したm_pInstanceに対して実体の生成と削除
各プログラムからはBuild時にできる実体に対してInstance関数を通じて実体にアクセスすることになります。
これでコピー辺りを禁止した複製されることのないクラスを作ることが出来るのです。
無理やり特殊な方法をとれば複製できないことは無いですが、流石にそこまでする人はいない(と信じたい)のでこれである程度安全なシングルトンが出来上がりです!
ゲーム開発では管理クラスをシングルトンにしたりする事が多いので、色んな場面で活躍してくれます。
簡単な割に覚えておくと絶対に役に立つのでお勧めのデザインパターンの一つです!
テンプレート化の方法
シングルトンはテンプレートで記述することもできます。
テンプレートを利用することで複数のシングルトンクラスを簡単に定義していくことができるようになります。
- template<class T>
- class Singleton
- {
- protected:
- // コンストラクタの外部での使用を禁止
- Singleton(){}
- virtual ~Singleton(){}
- private:
- // コピーコンストラクタの使用を禁止
- Singleton(const Singleton&);
- Singleton& operator = (const Singleton&);
- public:
- static void Build()
- {
- assert(!m_pInstance);
- m_pInstance = new T;
- }
- static void Release()
- {
- assert(m_pInstance);
- m_pInstance = null;
- }
- static T& Instance()
- {
- return *m_pInstance;
- }
- private:
- static T* m_pInstance;
- };
- template<class T>
- T* Singleton<T>::m_pInstance = NULL;
こんな感じでtemplate化が可能です。
そして実際にシングルトンのクラスを定義するには下記のように継承をする必要があります。
- class SingletonImpl
- : public Singleton<SingletonImpl>
- {
- friend class Singleton<SingletonImpl>;
- private:
- SingletonImpl()
- {}
- ~SingletonImpl()
- {}
- public:
- void Func()
- {
- printf("SingletonImpl::Func");
- }
- };
- void Func()
- {
- SingletonImpl::Build();
- SingletonImpl::Instance().Func();
- SingletonImpl::Release();
- }
シングルトンはどの程度までサポートするのがいいか議論する余地がありますが最低限これぐらいがあればいいかと思います(異論は認める!)
そもそもBuildとReleaseで作成と破棄を出来る時点で普通のシングルトンじゃないとかなんとか…。
フェニックスは何度でも蘇る。
今回のように何度でも作り直せるものをフェニックスシングルトンと呼びます。
また会社やプロジェクトによっては生成やコピーを実装的に禁止せずにプログラマ内でルール化している場合もそれなりにあります。
※確かUnrealEngine4のコードを見たときはSingletonだとコメントに記載しているだけで生成やコピーを禁止していなかった気がします。
又、どこでもアクセスできてしまうが故に誰が問題を起こしているのか?
マルチスレッド環境下での制御はどうなっているのか?
といった問題もあるので実際に使っていくうちに色々と発覚するデメリットもあります。
この辺りは話すと色んな所からツッコミが入って話が終わらなくなるのでここらへんにしときます。
それではみなさんもシングルトンを使いこなしてみてください!