シングルトンパターン
CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 91/104。
シングルトンパターンは、クラスのインスタンスがプログラム全体で1つだけであることを保証し、それへのグローバルなアクセスポイントを提供します。これは、設定マネージャー、ロガー、データベース接続プールなど、共有されるべきリソースに役立ちます。
主な考え方は、コンストラクタをprivateにして誰も直接インスタンスを作成できないようにし、単一のインスタンスを返す静的メソッドを提供することです。
#include <iostream>
#include <string>
class Logger {
private:
std::string logFile;
// プライベートコンストラクタ - 直接のインスタンス化を防ぐ
Logger() : logFile("app.log") {}
// コピーコンストラクタと代入演算子を削除
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
public:
// 単一のインスタンスにアクセスするための静的メソッド
static Logger& getInstance() {
static Logger instance; // 一度だけ作成され、プログラムが終了するまで保持される
return instance;
}
void log(const std::string& message) {
std::cout << "[LOG] " << message << "\n";
}
};
int main() {
Logger::getInstance().log("Application started");
Logger& logger = Logger::getInstance();
logger.log("Processing data");
}getInstance() 内の static Logger instance は、「マジックスタティック(magic statics)」と呼ばれる C++11 の機能です。これはスレッドセーフであり、一度だけ初期化されることが保証されています。コピーコンストラクタと代入演算子を削除することで、シングルトンのコピーが作成されるのを防ぎます。
シングルトン(Singleton)の使用は控えめにしてください。グローバルな状態を導入するため、テストが困難になる可能性があります。複数のインスタンスが存在すると問題が発生するような、真に共有されるリソースに最も適しています。
チャレンジ
簡単ゲームアプリケーションの設定を管理する GameSettings シングルトンを構築しましょう。これはシングルトンパターンの完璧なユースケースです。ゲームのすべての部分からアクセスできる設定マネージャーをただ1つだけ用意することで、全体を通して一貫した設定を保証したいからです。
コードは以下の3つのファイルに分けて構成します:
GameSettings.h: ゲーム設定を管理するシングルトンクラスを定義します。GameSettingsクラスは、3つの設定をプライベートメンバとして保持する必要があります:difficulty(string)、volume(0-100のinteger)、およびfullscreen(boolean)。これらをデフォルト値"Normal"、50、falseで初期化してください。シングルトンパターンを適切に実装してください:
- コンストラクタを
privateにする - コピーコンストラクタと代入演算子を削除(delete)する
- 唯一のインスタンスへの参照を返す静的な
getInstance()メソッドを提供する
設定を操作するために、以下のパブリックメソッドを追加してください:
setDifficulty(const std::string& diff)— 難易度を更新しますsetVolume(int vol)— 音量を更新しますsetFullscreen(bool fs)— フルスクリーンモードを更新しますdisplaySettings()— 現在のすべての設定を表示します
- コンストラクタを
GameModule.h: シングルトンにアクセスする別のモジュールを作成します。プリセット名 (string) を受け取る
applyGamePresetという関数を定義してください。この関数はGameSettingsシングルトンにアクセスし、プリセットに基づいて設定を行います:"casual"— 難易度を"Easy"、音量を70、フルスクリーンをfalseに設定します"competitive"— 難易度を"Hard"、音量を30、フルスクリーンをtrueに設定します- それ以外のプリセット — デフォルト設定をそのまま維持します
これにより、プログラムの異なる部分が同じシングルトンインスタンスにアクセスし、変更を加える方法が示されます。
main.cpp: すべてを組み合わせて、シングルトンの動作を確認します。2つの入力を読み取ります:
- プリセット名 (string)
- カスタム音量レベル (integer)
まず、シングルトンの
displaySettings()を呼び出して、初期のデフォルト設定を表示します。その後に空行を1つ出力してください。次に、
applyGamePreset関数を使用してプリセットを適用し、再度設定を表示します。その後に空行を1つ出力してください。最後に、入力されたカスタム値で音量のみを上書きし、最終的な設定を表示します。
displaySettings() メソッドは、以下の形式で正確に出力する必要があります:
Difficulty: [value]
Volume: [value]
Fullscreen: [yes/no]例えば、入力が casual と 85 の場合:
Difficulty: Normal
Volume: 50
Fullscreen: no
Difficulty: Easy
Volume: 70
Fullscreen: no
Difficulty: Easy
Volume: 85
Fullscreen: no入力が competitive と 20 の場合:
Difficulty: Normal
Volume: 50
Fullscreen: no
Difficulty: Hard
Volume: 30
Fullscreen: yes
Difficulty: Hard
Volume: 20
Fullscreen: yesmain.cpp と GameModule.h の両方が同じシングルトンインスタンスにアクセスしていることに注目してください。一方で行われた変更は、どこからでも確認できます。これが、ゲーム設定のような共有リソースにおけるシングルトンパターンの強力な点です。
チートシート
シングルトン・パターン(Singleton pattern)は、プログラム全体でクラスのインスタンスが1つだけであることを保証し、それへのグローバルなアクセスを提供します。設定マネージャー、ロガー、データベース接続プールなどの共有リソースに役立ちます。
主な実装手順:
- 直接のインスタンス化を防ぐために、コンストラクタをprivateにする
- コピーを防止するために、コピーコンストラクタと代入演算子を削除(delete)する
- 唯一のインスタンスへの参照を返す静的メソッドを提供する
class Logger {
private:
std::string logFile;
// privateコンストラクタ
Logger() : logFile("app.log") {}
// コピーコンストラクタと代入演算子を削除
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
public:
// 唯一のインスタンスにアクセスするための静的メソッド
static Logger& getInstance() {
static Logger instance; // 一度だけ作成され、プログラムが終了するまで生存する
return instance;
}
void log(const std::string& message) {
std::cout << "[LOG] " << message << "\n";
}
};
// 使用例
Logger::getInstance().log("Application started");
Logger& logger = Logger::getInstance();
logger.log("Processing data");getInstance() 内の static Logger instance は、C++11の「マジックスタティック(magic statics)」を使用しています。これはスレッドセーフであり、一度だけ初期化されることが保証されています。
シングルトンはグローバルな状態を導入し、テストを困難にする可能性があるため、控えめに使用してください。複数のインスタンスが存在すると問題が発生するような、真に共有されるリソースに最適です。
自分で試してみよう
#include <iostream>
#include <string>
#include "GameSettings.h"
#include "GameModule.h"
using namespace std;
int main() {
// 入力を読み込む
string presetName;
int customVolume;
cin >> presetName;
cin >> customVolume;
// TODO: GameSettings のシングルトンインスタンスを取得する
// TODO: 初期のデフォルト設定を表示する
// TODO: 空行を出力する
// TODO: applyGamePreset 関数を使用してプリセットを適用する
// TODO: プリセット適用後の設定を表示する
// TODO: 空行を出力する
// TODO: 音量を customVolume で上書きする
// TODO: 最終的な設定を表示する
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
3コンストラクタとデストラクタ
デフォルトコンストラクタ引数付きコンストラクタコピーコンストラクタムーブコンストラクタコンストラクタ初期化リスト委譲コンストラクタデストラクタ詳解Rule of Three / Five / Zeroまとめ - Stringクラス6演算子オーバーロード
演算子オーバーロード入門算術演算子のオーバーロード比較演算子のオーバーロードストリーム演算子代入演算子のオーバーロード[] と () 演算子のオーバーロード型変換演算子まとめ:Matrix クラス