「C++のデザインパターン - シングルトン」の版間の差分

ナビゲーションに移動 検索に移動
編集の要約なし
(ページの作成:「== 概要 == <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;  // コピーコンストラクタを禁止
     static Singleton* m_Instance;
     Singleton& operator=(const Singleton&) = delete; // 代入演算子を禁止
   
   
     int m_Data = 1;
public:
     ~Singleton()
    {
        if (instance != nullptr) {
            delete instance;
            instance = nullptr;
        }
    }
   
   
public:
     // インスタンスを取得するメソッド
     // インスタンスを取得するメソッド
     static Singleton* getInstance()
     static Singleton* getInstance()
58行目: 85行目:
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<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++]]

案内メニュー