問題の歴史:
シングルトンパターンは、特定のクラスのインスタンスが1つだけ作成されるように制限するために提案されました。これは、グローバルマネージャー(例えば、ロガー、リソースプール、設定者)を実装するために求められていました。
問題:
シングルトンの実装は簡単に見えますが、C++では問題があります:スレッド安全性、破棄の正確性、初期化の順序。
解決策:
シングルトンを実装する最も安全で現代的な方法は、静的関数内に静的ローカル変数を使用する方法で、これにより最初の呼び出し時に初期化が保証され、スレッド安全性が確保され(C++11以降)、正確な破棄が可能です。
コード例:
class Singleton { public: static Singleton& instance() { static Singleton s; return s; } void doSomething() {} private: Singleton() {} Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; };
主な特徴:
シリアライズやクローンでシングルトンの2番目のインスタンスを作成できますか?
はい。シリアライズ/デシリアライズメソッドを実装したり、コピーコンストラクタを制限せずに手動でclone()を実装した場合、2番目のインスタンスが作成される可能性があります。これを避けるために、すべてのコピー、クローン、およびシリアライズによる復元の方法を禁止する必要があります。
C++98/03標準では、ローカル静的変数を使用してスレッドセーフなシングルトンが正しく実装されますか?
いいえ。C++11以前のローカル静的変数は初期化時のスレッド安全性を保証していませんでした。これにより、2つのスレッドが同時にインスタンス()関数に入ると、複数のインスタンスが作成される可能性があります。C++11以降、これは標準レベルで解決されています。
静的ローカル変数を通じて作成されたシングルトンのインスタンスは、いつ破棄されますか?
オブジェクトは作成の逆順(LIFO)でプログラムの終了時に破棄されます。しかし、デストラクタで既に破棄されたオブジェクトにアクセスがあると問題が発生する可能性があります。
ロギングシステムで、開発者はグローバルポインタとnewを使用してシングルトンを実装し、コピーを禁止するのを忘れます。プログラムは動作しますが、マルチスレッド環境では時折異なるロガーのインスタンスが生成され、メッセージが失われます。
長所:
短所:
シングルトンは静的ローカル変数を介して静的関数内で実装され、コピーを禁止されています。スレッド安全性が保証され、プログラムはスケールし、ログは正しく分割されます。
長所:
短所: