C++ ゲームプログラム シングルトン


シングルトンとは?

シングルトンとは、オブジェクト指向における特殊なクラス設計の一つ。

簡単に解説すると…
そのクラスのインスタンスが1つしか存在しないようにする代わりに、オブジェクト参照の必要を無くすことができる。

シングルトンではない普通のクラス

メンバ変数にint型aを持ったTestクラス
例)Testクラスのaを2倍する処理

もしTestクラスがプログラム実行中に1つしか存在しない、と決めれば引数にインスタンスを指定する必要が無くなる!(引数の削減)

Testクラスのメンバ変数aを2倍にしたい!(エラー)

このままだとメンバ変数aにオブジェクト参照がないためエラーが出る。

なら、Testクラス内にTestクラスのインスタンスを保持しておけば良い!!

シングルトンクラスのクラス設計

①自身のインスタンスを保持しておく変数を作る

class Test
{
public:

	//コンストラクタ
	Test() {

	}

	//デストラクタ
	~Test() {

	}

	//メンバ変数
	int a = 0;

private:

	//シングルトンクラスを保存しておく変数
	static Test* instance;
};

static Test* instance を追加した。クラスが生成された時にこの変数にインスタンスを保存しておく。

staticとは…
静的メンバ変数。オブジェクト参照を無くす代わりに、同じクラス全体で変数の中身が同じになる。この仕組みを利用してシングルトンクラスのインスタンスをオブジェクト参照なしに持ってこれる。

②コンストラクタを作成する

class Test
{
public:

	//コンストラクタの代わり
	static void Init() {

		//既にインスタンスが存在すれば
		if (instance != nullptr) {
			return;
		}

		//シングルトンクラスを生成する
		instance = new Test();
	}

	//デストラクタ
	~Test() {

	}

	//メンバ変数
	int a = 0;

private:

	//コンストラクタ
	Test() {

	}

	//シングルトンクラスを保存しておく変数
	static Test* instance;
};

既存のコンストラクタをprivateに移し、コンストラクタの代わりとなる
Init関数を作成する。
既存のコンストラクタをprivateにした理由はクラス外でnew演算子を使わせない為である。

コンストラクタをprivateにしたため、new演算子が使えなくなった!!

これにより外部からインスタンスを作成させないようにする。
つまり、内部でインスタンスを作成しろ!ってこと。そのためにInit関数を作成する。

    //既にインスタンスが存在すれば
		if (instance != nullptr) {
			return;
		}

		//シングルトンクラスを生成する
		instance = new Test();

if文「既にインスタンスが存在すれば」は、既に初期化したのにさらに初期化しようとした場合のエラー処理(これを書かないとメモリリークが発生する)。
クラス内なのでメンバ変数instanceに対してnew演算子を使ってインスタンスを作成する。

③デストラクタを作成する

class Test
{
public:

	//コンストラクタの代わり
	static void Init() {

		//既にインスタンスが存在すれば
		if (instance != nullptr) {
			return;
		}

		//シングルトンクラスを生成する
		instance = new Test();
	}

	//デストラクタの代わり
	static void UnInit() {

		//インスタンスが存在すれば
		if (instance) {

			//削除する
			delete instance;

			instance = nullptr;
		}
	}


	//メンバ変数
	int a = 0;

private:

	//コンストラクタ
	Test() {

	}

	//デストラクタ
	~Test() {

	}

	//シングルトンクラスを保存しておく変数
	static Test* instance;
};

既存のデストラクタもprivateに移す。また代わりにUnInit関数を作成する。
メンバ変数instanceがprivateなので外部からdelete処理が出来ない為、UnInit関数を作成する。

④インスタンスを取得するゲッターを作成する

class Test
{
public:

	//コンストラクタの代わり
	static void Init() {

		//既にインスタンスが存在すれば
		if (instance != nullptr) {
			return;
		}

		//シングルトンクラスを生成する
		instance = new Test();
	}

	//デストラクタの代わり
	static void UnInit() {

		//インスタンスが存在すれば
		if (instance) {

			//削除する
			delete instance;

			instance = nullptr;
		}
	}

	//インスタンスを取得するゲッター
	static Test* Instance() {
		return instance;
	}

	//メンバ変数
	int a = 0;

private:

	//コンストラクタ
	Test() {

	}

	//デストラクタ
	~Test() {

	}

	//シングルトンクラスを保存しておく変数
	static Test* instance;
};

メンバ変数instanceを取得する為にゲッターを作成した。

⑤仕上げ

Test* Test::instance = nullptr;

static変数は初期化しないといけないが、クラスの定義内で初期化ができない。
そのためクラス外で初期化(nullptrを代入)する必要がある

完成

全体像はこちら

class Test
{
public:

	//コンストラクタの代わり
	static void Init() {

		//既にインスタンスが存在すれば
		if (instance != nullptr) {
			return;
		}

		//シングルトンクラスを生成する
		instance = new Test();
	}

	//デストラクタの代わり
	static void UnInit() {

		//インスタンスが存在すれば
		if (instance) {

			//削除する
			delete instance;

			instance = nullptr;
		}
	}

	//インスタンスを取得するゲッター
	static Test* Instance() {
		return instance;
	}

	//メンバ変数
	int a = 0;

private:

	//コンストラクタ
	Test() {

	}

	//デストラクタ
	~Test() {

	}

	//シングルトンクラスを保存しておく変数
	static Test* instance;
};

Test* Test::instance = nullptr;

※メンバ変数aは仮であるため書く必要ない

使い方


引数がなくなった!

シングルトンクラスを使うときにInstance関数を書く代わりに、オブジェクト参照を無くすことができた!

有効な使い道は…?

Playerクラス
Gameクラス
○○Managerクラスなど…
実行中に一つしか存在しないクラスは意外と多い…


追記:同一インスタンスを取得するコード

#define SAFE_DELETE(p)	{if(p){delete p;p=nullptr;}}	//delete処理のマクロ
template<typename T>
class Singleton
{
public:
	static void Init() { if (!instance)instance = new T(); }	//インスタンスの初期化
	static void UnInit() { SAFE_DELETE(instance); }				//インスタンスの削除
	static T* Instance() { return instance; }					//インスタンスのゲッター

protected:
	Singleton() {}
	virtual ~Singleton() {}

private:
	static T* instance;
};

template<typename T>
T* Singleton<T>::instance = nullptr;

このクラスを継承するだけで同一のインスタンスを取得できる

ただしシングルトンクラスのようにクラスがただ一つになるとは限らない

この記事が気に入ったらサポートをしてみませんか?