13,024
回編集
(ページの作成:「== 概要 == <br><br> == サンプルコード == 以下の例では、コンストラクタは<code>private</code>で隠蔽して、<code>new</code>の代わりにSingleton::getInstanceメソッドでインスタンスを取得する。<br> privateメンバ変数static Singleton* instanceで自身のインスタンスのポインタを保持している。<br> <br> クラス外部で、Singleton* Singleton::instance = 0;により、自身のインスタンスの…」) |
編集の要約なし |
||
| 1行目: | 1行目: | ||
== 概要 == | == 概要 == | ||
シングルトンパターンは、クラスのインスタンスが1つだけ存在することを保証するデザインパターンである。<br> | |||
<br> | |||
シングルトンパターンの特徴を以下に示す。<br> | |||
* プライベートコンストラクタによる外部からのインスタンス化を防止する。 | |||
* 静的メソッドによる唯一のインスタンスへのアクセス提供する。 | |||
* マルチスレッド環境での同期処理の必要性 | |||
<br> | |||
シングルトンパターンの使用場面を以下に示す。<br> | |||
* 設定マネージャー | |||
* データベース接続管理 | |||
* ロギングシステム | |||
* ファイルシステム | |||
<br> | |||
しかし、グローバル状態を作り出すため単体テストの難しさやコードの結合度が高くなる等の課題もある。<br> | |||
そのため、使用は慎重に検討する必要がある。<br> | |||
<br> | |||
実装方法には、ポインタベース、参照ベース、C++11以降では静的局所変数を利用した方法等がある。<br> | |||
<br> | |||
マルチスレッド対応が必要な場合は、double-checked lockingパターンや静的局所変数の初期化の特性を利用した実装 (C++11以降) も可能である。<br> | |||
<br><br> | <br><br> | ||
== | == ポインタベースのシングルトンクラス == | ||
以下の例では、コンストラクタは<code>private</code>で隠蔽して、<code>new</code>の代わりにSingleton::getInstanceメソッドでインスタンスを取得する。<br> | 以下の例では、コンストラクタは<code>private</code>で隠蔽して、<code>new</code>の代わりにSingleton::getInstanceメソッドでインスタンスを取得する。<br> | ||
privateメンバ変数static Singleton* instanceで自身のインスタンスのポインタを保持している。<br> | privateメンバ変数static Singleton* instanceで自身のインスタンスのポインタを保持している。<br> | ||
| 17行目: | 35行目: | ||
{ | { | ||
private: | private: | ||
static Singleton* m_Instance; // インスタンスのポインタ | |||
int m_Data = 1; | |||
// シングルトンのインスタンス生成にはgetInstanceメソッドを使用するため、コンストラクタを隠蔽する | // シングルトンのインスタンス生成にはgetInstanceメソッドを使用するため、コンストラクタを隠蔽する | ||
Singleton() | Singleton() | ||
| 23行目: | 44行目: | ||
} | } | ||
// | Singleton(const Singleton&) = delete; // コピーコンストラクタを禁止 | ||
Singleton& operator=(const Singleton&) = delete; // 代入演算子を禁止 | |||
public: | |||
~Singleton() | |||
{ | |||
if (instance != nullptr) { | |||
delete instance; | |||
instance = nullptr; | |||
} | |||
} | |||
// インスタンスを取得するメソッド | // インスタンスを取得するメソッド | ||
static Singleton* getInstance() | static Singleton* getInstance() | ||
| 58行目: | 85行目: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br> | <br> | ||
上記のクラスの使用例を、以下に示す。<br> | |||
<syntaxhighlight lang="c++"> | <syntaxhighlight lang="c++"> | ||
#include <iostream> | #include <iostream> | ||
| 98行目: | 125行目: | ||
インスタンスBのdata: 10 | インスタンスBのdata: 10 | ||
<br><br> | <br><br> | ||
== 参照ベースのシングルトンクラス == | |||
参照ベースのシングルトンクラスは、静的局所変数方式とポインタベース方式の中間的なアプローチである。<br> | |||
<br> | |||
メモリ管理の注意が必要となり、明示的なデストラクタの実装やスマートポインタの使用すべき場合がある。<br> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
class Singleton | |||
{ | |||
private: | |||
Singleton() = default; | |||
static Singleton* createInstance() | |||
{ | |||
return new Singleton(); | |||
} | |||
public: | |||
Singleton(const Singleton&) = delete; | |||
Singleton& operator=(const Singleton&) = delete; | |||
static Singleton& getInstance() | |||
{ | |||
static Singleton& instance(*createInstance()); | |||
return instance; | |||
} | |||
}; | |||
</syntaxhighlight> | |||
<br><br> | |||
== 静的局所変数を使用したシングルトンクラス (C++11以降) == | |||
静的局所変数を使用したシングルトンクラスは、以下に示すメリットがある。<br> | |||
* スレッドセーフ (C++11規格で保証) | |||
* メモリリーク防止 | |||
* nullポインタチェックが不要 | |||
* デストラクタの明示的実装が不要 | |||
* より安全で簡潔な実装 | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
class Singleton | |||
{ | |||
private: | |||
Singleton() = default; | |||
public: | |||
Singleton(const Singleton&) = delete; | |||
Singleton& operator=(const Singleton&) = delete; | |||
static Singleton& getInstance() | |||
{ | |||
static Singleton instance; | |||
return instance; | |||
} | |||
}; | |||
</syntaxhighlight> | |||
<br><br> | |||
== double-checked lockingパターンのシングルトンクラス (マルチスレッド対応) == | |||
double-checked lockingパターンのシングルトンクラスでは、以下に示す特徴がある。<br> | |||
* atomicを使用したスレッドセーフな実装 | |||
* メモリバリアによる適切な同期 | |||
* 最初のチェックでロックを避けることによるパフォーマンス向上 | |||
* 2重チェックによる安全性確保 | |||
<br> | |||
<u>※注意</u><br> | |||
<u>ただし、この実装は複雑で微妙なバグが発生しやすいため、可能であればC++11以降の静的局所変数による実装を推奨する。</u><br> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
#include <mutex> // mutex機能とlock_guardを使用するために必要 | |||
#include <atomic> // アトミック操作とメモリオーダーを使用するために必要 | |||
#include <memory> // スマートポインタを使用する場合に必要 | |||
class Singleton | |||
{ | |||
private: | |||
// シングルトンインスタンスへのアクセスを同期するためのミューテックス | |||
static std::mutex mutex_; | |||
// シングルトンインスタンスへのアトミックポインタ | |||
// アトミック操作により、マルチスレッド環境での安全性を確保 | |||
static std::atomic<Singleton*> instance_; | |||
// プライベートコンストラクタ (外部からのインスタンス化を防止) | |||
Singleton() = default; | |||
public: | |||
// コピーコンストラクタを削除 (シングルトンの複製を防止) | |||
Singleton(const Singleton&) = delete; | |||
// 代入演算子を削除 (シングルトンの代入を防止) | |||
Singleton& operator=(const Singleton&) = delete; | |||
// シングルトンインスタンスを取得するスレッドセーフな関数 | |||
static Singleton* getInstance() | |||
{ | |||
// 最初の確認 : ロック無しでインスタンスの存在を確認 | |||
Singleton* tmp = instance_.load(std::memory_order_relaxed); | |||
// メモリバリア : 他のスレッドの変更を確実に認識 | |||
std::atomic_thread_fence(std::memory_order_acquire); | |||
if (tmp == nullptr) { | |||
// ロックを取得して2重チェック | |||
std::lock_guard<std::mutex> lock(mutex_); | |||
tmp = instance_.load(std::memory_order_relaxed); | |||
// 2重チェック : 他のスレッドがインスタンスを生成していないことを確認 | |||
if (tmp == nullptr) { | |||
// 新しいインスタンスを生成 | |||
tmp = new Singleton; | |||
// メモリバリア : インスタンスの作成を他のスレッドに確実に通知 | |||
std::atomic_thread_fence(std::memory_order_release); | |||
// 生成したインスタンスを保存 | |||
instance_.store(tmp, std::memory_order_relaxed); | |||
} | |||
} | |||
return tmp; | |||
} | |||
}; | |||
// 静的メンバ変数の初期化 | |||
std::mutex Singleton::mutex_; | |||
std::atomic<Singleton*> Singleton::instance_{nullptr}; | |||
</syntaxhighlight> | |||
<br><br> | |||
__FORCETOC__ | __FORCETOC__ | ||
[[カテゴリ:C++]] | [[カテゴリ:C++]] | ||