C++ デザイン パターン作成シングルトン パターン

I. 概要

        シングルトン パターンは、単一状態パターンとも呼ばれ、オブジェクト インスタンスを 1 つだけ生成できるクラスを作成するために使用される作成パターンです。たとえば、プロジェクト内にはサウンド管理システム、構成システム、ファイル管理システム、ログ システムなどが 1 つだけあり、Windows オペレーティング システム全体を 1 つのプロジェクトとみなしたとしても、タスクは 1 つだけです。マネージャーウィンドウなどがあります。シングルトン モードを導入する実装の目的は、クラスのインスタンスが 1 つだけ存在することを保証し、そのインスタンスにアクセスできるグローバル メソッドを提供することです。

2. シングルトンモードの分類

1. レイジーモード

1) コード例

class CSingletonImpl
{ public:     static CSingletonImpl* GetInstance()     {         if (m_pInstance == nullptr)         {             m_pInstance = new CSingletonImpl;         }






        m_pInstance を返します。
    プライベート
:
    CSingletonImpl(){};
    ~CSingletonImpl(){};
    CSingletonImpl(const CSingletonImpl& the);
    CSingletonImpl& 演算子=(const CSingletonImpl& その他);
プライベート:
    静的 CSingletonImpl* m_pInstance;
};

CSingletonImpl*CSingletonImpl::m_pInstance = nullptr;

2) 説明

マルチオブジェクトの問題を防ぐために、シングルトン モードでは、コンストラクター、デストラクター、コピー コンストラクター、および代入演算子の関数をプライベートとして設定し、オブジェクトを作成するためのパブリックの一意のインターフェイス メソッドを設定し、クラスの静的ポインターを定義します。これは一般的なアプローチですが、何が間違っているのでしょうか? シングルスレッドで使用する場合には問題ありませんが、複数のスレッドで使用すると問題が発生する可能性があり、オペレーティング システムのタイム スライス スケジューリングの問題により複数のスレッドが切り替わり、複数のオブジェクトが生成される場合は、次の解決策が必要です。この問題は、GetInstance() メンバー関数のシャックルを使用することです。

サンプルコード:

プライベートメンバー変数を追加します: static std::mutex m_mutex;

static CSingletonImpl* GetInstance()
{

 m_mutex.lock();
 if (m_pInstance == nullptr)
 {   m_pInstance = 新しい CSingletonImpl;  m_mutex.unlock  ();


 m_pInstance を返します。
}

上記のコードを追加しても問題ないでしょうか?コードのロジックには問題はなく、インターフェイス関数をロックすることでスレッドの安全性も確保されていますが、実行効率の点では大きな問題があります。プログラムの実行中、GetInstance() は複数のスレッドによって頻繁に呼び出される可能性があり、各呼び出しではロックとロック解除のプロセスが実行されます。これはプログラムの実行効率に重大な影響を及ぼします。また、ロック メカニズムは最初のスレッドに対してのみ意味を持ちます。オブジェクトの作成。オブジェクトが作成されると、そのオブジェクトは読み取り専用オブジェクトになります。マルチスレッドでは、読み取り専用オブジェクトへのアクセスをロックすることはコストがかかるだけでなく、無意味でもあります。では、この問題をどうやって解決すればいいのでしょうか?これは、次のメカニズム関数実装コードに基づく二重ロック メカニズムです。

    static CSingletonImpl* GetInstance()
    {         if (m_pInstance == nullptr)         {             std::lock_guard<std::mutex> siguard(si_mutex);             if (m_pInstance == nullptr)             {                 m_pInstance = 新しい CSingletonImpl;             m_pInstance を返し         ます         。     }









上記の二重ロック機構は完璧に見えますが、実際には潜在的な問題があります。メモリ アクセスの順序変更は二重ロックの失敗につながります。推奨される方法は、新しい C++11 標準のいくつかの機能です。サンプル コードは次のとおりです。次のように:

#include <ミューテックス>
#include <アトミック>

// 通過原子变量解决双重锁定底層层问题(load,store)
class CSingletonImpl
{ public:     static CSingletonImpl* GetInstance()     {         CSingletonImpl* task = m_taskQ.load(std::memory_order_relaxed);          std::atomic_thread_fence(std::memory_order_acquire);         if (task == nullptr)         {             std::lock_guard<std::m_mutex> lock(m_mutex);             task = m_taskQ.load(std::memory_order_relaxed);              if (タスク == nullptr)             {                 タスク = 新しい CSingletonImpl;                 std::atomic_thread_fence(std::memory_order_release);                 m_taskQ.store(タスク, std::memory_order_relaxed);














            タスクを返し
        ます
        。
    プライベート
:
    CSingletonImpl(){};
    ~CSingletonImpl(){};
    CSingletonImpl(const CSingletonImpl& the);
    CSingletonImpl& 演算子=(const CSingletonImpl& その他);
プライベート:
    静的 std::mutex m_mutex;
    静的 std::atomic<CSingletonImpl*> m_taskQ;
};

std::mutex CSingletonImpl::m_mutex;
std::atomic<CSingletonImpl*> CSingletonImpl::m_taskQ;

2. ハングリーマンモード

1) サンプルコード

class CSingletonImpl
{ public:     static CSingletonImpl* GetInstance()     {         return m_pInstance;     プライベート:     CSingletonImpl(){};     ~CSingletonImpl(){};     CSingletonImpl(const CSingletonImpl& the);     CSingletonImpl& 演算子=(const CSingletonImpl& その他); プライベート:     静的 CSingletonImpl* m_pInstance; };












CSingletonImpl*CSingletonImpl::m_pInstance = new CSingletonImpl();

2) 説明

このようなモードはハングリーチャイニーズスタイルと呼ぶことができます。 -------------------------------------------------- -------------------------------------------------- ---------------------- GetInstance() メンバー関数が呼び出されるかどうかに関係なく、プログラムが実行されるとすぐにシングルトン クラス オブジェクトが作成されます。 。Hungry スタイルのシングルトン クラス コードの実装では、プロジェクト内に複数の .cpp ソース ファイルがあり、これらのソース ファイルにグローバル変数の初期化コードが含まれている場合、たとえば次のコードが .cpp に存在する可能性があることに注意する必要があります。 CP:

int g_test = CSingletonImpl::GetInstance()->m_i; //m_i は int 型変数です

その場合、そのようなコードは安全ではありません。複数のソース ファイル内のグローバル変数の初期化順序が不確実であるため、GetInstance() 関数が nullptr を返す可能性が高く、この時点で m_i メンバー変数にアクセスすると、間違いなくプログラムの異常実行が発生します。 。したがって、ハングリーな中国式シングルトン クラス オブジェクトの使用は、main 関数などのプログラム エントリ関数の後に実行する必要があります。

注: 関数の初回実行時に初期化される静的変数、およびコンパイラ定数によって初期化される基本型の静的変数の場合、シングルトン クラスのデストラクター内で他のシングルトン クラスのオブジェクトを参照しないでください。

おすすめ

転載: blog.csdn.net/leiyang2014/article/details/132087371