C++設計モード(一)単例モード


C++モノリシックモードは、モノリシックモード、モノリシックモードとも呼ばれる.単一のインスタンス・モードを使用して、クラスに1つのインスタンスしかないことを保証し、すべてのプログラム・モジュールで共有されるグローバル・アクセス・ポイントにアクセスします.システムのログ出力など、このような機能モジュールが必要なところがたくさんあります.単例モードには多くの実装方法があり、
a.怠け者式:使用時に作成するが、マルチスレッドアクセス時にスレッドが安全でない(二重ロック)b.餓漢式:クラスファイルがロードされた時にオブジェクトが作成され、オブジェクトがずっと使用されていない場合、クラスオブジェクトは空間を浪費する
特徴と選択:
  • スレッド同期を行う場合、アクセス量が大きい場合、またはアクセス可能なスレッドが多い場合は、餓漢実装を採用し、より良いパフォーマンスを実現することができます.これは空間で時間を変えます.
  • はアクセスが少なく、怠け者で実現しています.これは時間で空間を変えます.

  • C++では、グローバル変数を直接使用することもできますが、このようなコードは優雅ではありません.クラスのプライベート静的ポインタ変数を使用してクラスの一意のインスタンスを指し、共通の静的メソッドでインスタンスを取得する単一のクラスを定義します.次のクラス定義のようにします.
    //    (       new  )       ,     
    class CSingleton
    {  
    	//       
    public:  
    	static CSingleton * GetInstance()  
    	{  
    		if (m_pInstance == NULL)  
    			m_pInstance = new CSingleton();
    		return m_pInstance;  
    	}  
    	//                  ,   ,      ,               
    	/*~CSingleton()
    	{
    		if( m_pInstance != NULL)
    		{
    			delete  m_pInstance;
    			 m_pInstance = NULL;
    		}
    	
    		cout << "~CSingleton()" << endl;
    	}
        */
    private:  
    	CSingleton(){};  
    	static CSingleton * m_pInstance;
    };

    C++単例モードクラスCSingletonには以下の特徴がある.
  • 一意のインスタンスを指す静的ポインタm_pInstance、プライベートです.
  • このユニークなインスタンスを取得し、必要に応じてインスタンスを作成する共通の関数があります.
  • その構造関数はプライベートであり、クラスのインスタンスを別の場所から作成することはできません.

  • 多くの場合、このような実装では問題は発生しませんが、設計classは設計typeのように初期化と破棄を考慮する必要があります.また見に来ますm_pInstanceが指す空間はいつ解放されますか?さらに深刻な問題は、このインスタンスのプロファイル操作がいつ実行されるかということです.ファイルを閉じたり、外部リソースを解放したりするなど、クラスのプロファイル動作に必要な操作がある場合、上記のコードはこの要件を達成できません.インスタンスを正常に削除する方法が必要です.
    なぜ構造関数でm_を解放しないのかpInstanceは?
  • 1、単一の例におけるnewのオブジェクトはdelete解放を必要とする.
  • 2、deleteがオブジェクトを解放すると、オブジェクトの構造関数が呼び出されます.
  • 3、もし構造関数の中でdeleteを呼び出すならば、プログラムが終わる時、構造関数を全然入れないで、どうしてdeleteができますか.
  • 4、プログラムが終了して自動的に解析できると、1つの解析のループが悪くなるので、newはdeleteに対応します.

  • プログラムの終了時にGetInstanceを呼び出し、返されたポインタに対してdelete操作を呼び出すことができます.これにより機能が実現しますが、醜いだけでなく、エラーが発生しやすいです.このような付加コードは忘れやすく、delete後にGetInstance関数を呼び出すコードがないことも保証しにくいからです.適切な方法は、このクラスに適切な時に自分を削除することを自分で知ってもらうことです.あるいは,自分の削除操作をシステム内の適切な点に掛け,適切なときに自動的に実行させる.プログラムが終了すると,システムはすべてのグローバル変数を自動的に解析することを知っている.実際、システムは、これらの静的メンバーもグローバル変数であるように、すべてのクラスの静的メンバー変数を解析します.この特徴を用いて,このような静的メンバー変数を単一クラスで定義することができ,その唯一の作業は,構造関数で単一クラスのインスタンスを削除することである.次のコードのGarbageクラスのように:
    //    (       new  )       ,     
    class CSingleton
    {  
        //       
    public:  
        static CSingleton * GetInstance()
    	{  
    		if (m_pInstance == NULL)  
    			m_pInstance = new CSingleton();
    		return m_pInstance;  
    	} 
        void print() { cout <

    クラスGarbageは、クラスが他の場所で乱用されないようにCSingletonのプライベート埋め込みクラスとして定義される.プログラムの実行が終了すると、CSingletonの静的メンバーGarbageの解析関数が呼び出され、この解析関数は単一のインスタンスの一意のインスタンスを削除します.この方法でC++シングルモードオブジェクトを解放するには、次のような特徴があります.
  • は、単一のクラス内で固有のネストされたクラスを定義する.
  • は、単一のクラス内で、解放に特化したプライベートな静的メンバーを定義する.
  • プログラムは、終了時にグローバル変数の特性を解析し、最終的な解放タイミングを選択する.
  • C++シングルモードのコードを使用すると、オブジェクトの解放に関心を持つ必要はありません.

  • しかし、これは完璧な実現ですか?いや!この方法はスレッドが安全ではありません.2つのスレッドがGetInstanceメソッドを同時に初めて呼び出し、pがNULL値であることを同時に検出することを考慮すると、2つのスレッドは同時にpにインスタンスを構築します.これは深刻なエラーです.スレッドの安全を保証するために、ロックを使用できます.
    //         (   )
    /*
                ,   :       getInstance       ,    
      ,      ,         ,              ,     
      。       ,          ,     ,            
      ,       。    ,         ,              
             。
    */
    class CSingleton
    {  
        //       
    public:  
        static CSingleton * GetInstance()
    	{
    		if( m_pInstance == NULL)
    		{
    			pthread_mutex_lock(&mutex);  //  
    			if( m_pInstance == NULL)
    				 m_pInstance = new CSingleton();
    			pthread_mutex_unlock(&mutex);
    		}
    		return  m_pInstance;
    	}
    
    	static pthread_mutex_t mutex;
    private:  
    	CSingleton()
    	{
    		pthread_mutex_init(&mutex, NULL);
    	} 
        static CSingleton * m_pInstance;  
    
        class Garbage //                  CSingleton     
        {  
        public:  
    		~Garbage ()  
    		{  
    			if (CSingleton::m_pInstance)
    			{ 
    				delete CSingleton::m_pInstance;
    				CSingleton::m_pInstance = NULL;
    			} 
    		}
    	};  
    
    	static Garbage garbage; //         ,      ,             
    };
    
    CSingleton* CSingleton:: m_pInstance= NULL;
    pthread_mutex_t   CSingleton::mutex;  
    CSingleton::Garbage CSingleton::garbage;

    さらに、餓漢の実装方法を見てみましょう.すなわち、クラスのインスタンスを呼び出すかどうかにかかわらず、プログラムの開始時にクラスのインスタンスが生成され、後でこのインスタンスのみが返されます.
    静的初期化インスタンスによってスレッドのセキュリティが保証されます.WHY?静的インスタンス初期化は、プログラムの開始時にメイン関数に入る前にプライマリスレッドによって単一スレッドで初期化されるため、マルチスレッドの問題を心配する必要はありません.
    したがって、パフォーマンスの要件が高い場合は、頻繁なロック競合を回避するために、このモードを使用する必要があります.
    //   (          new  )      
    class CSingleton
    {  
        //       
    public:  
        static CSingleton * GetInstance()
    	{
    		return  m_pInstance;
    	}
    
    private:  
    
        class Garbage //                  CSingleton     
        {  
        public:  
    		~Garbage ()  
    		{  
    			if (CSingleton::m_pInstance)
    			{ 
    				delete CSingleton::m_pInstance;
    				CSingleton::m_pInstance = NULL;
    			} 
    		}
    	};  
    	CSingleton(){}; 
        static CSingleton * m_pInstance;
    	static Garbage garbage; //         ,      ,             
    };
    
    CSingleton* CSingleton::m_pInstance = new CSingleton(); 
    CSingleton::Garbage CSingleton::garbage;