c++における静的関数と動的関数の違いと単例モードとの関係

2720 ワード

参照先:
https://zhuanlan.zhihu.com/p/37469260
https://www.cnblogs.com/zxh1210603696/p/4157294.html
https://blog.csdn.net/Wu_qz/article/details/81044823
static:
  • 静的データ・メンバー
  • 1、クラス内のデータメンバーの前にstaticキーを付ける.すなわち、静的データメンバー2、クラス静的データメンバーの場合、クラスのオブジェクトが何個あっても、その静的データメンバーはメモリにコピーが1部しかない(他の一般的なデータメンバーは、各クラスオブジェクトに独自のメモリコピーがある).この静的データメンバーは、すべてのクラスオブジェクトによって共有される3、静的データ・メンバーはグローバル・データ領域に格納され、定義時に記憶領域が割り当てられ、プログラム実行終了時に破棄される4、静的データ・メンバーはクラス内で定義および初期化できない.クラス内でのみ宣言され、クラス外で定義および初期化され、デフォルトの初期化は0 5、静的データ・メンバーの初期化は:=6、静的データ・メンバーはpublic private protectedアクセス・ルール7に従い、静的データ・メンバーはクラス名と役割ドメイン演算子(:)を直接使用してアクセスできます:(アクセス・ルールが許可されている場合)
  • 静的メンバー関数
  • 1、通常クラスメンバー関数の前にstaticキーを付ける、すなわち静的メンバー関数2、クラス外で静的メンバー関数を定義する場合、staticキーを追加する必要はなく、クラスで宣言するときに追加すればよい3、静的メンバー関数は静的データメンバーと静的メンバー関数のみにアクセスでき、通常メンバー関数は静的メンバー関数と静的データメンバー4、静的メンバー関数はクラスに属し、いずれのクラスオブジェクトにも属しません5、静的メンバー関数はthisポインタ6がありません.
    静的メンバー関数を使用するメリット:
    1、他のファイルで同じ名前の関数を定義でき、衝突は発生しません.2、静的関数は他のファイルでは使用できません. 
    単一モードの実装
    シングル・インスタンス・モードは、1つのクラスに1つのインスタンスしかないことを保証し、すべてのプログラム・モジュールで共有できるグローバル・アクセス・ポイントを提供します.
    単一のモードはstatic関数によって実装されることが多いが、その実装手順は一般的に以下の通りである.
  • はその構造関数を私有化し、外部から単一のクラスのオブジェクトを作成することを防止する.
  • クラスのプライベート静的ポインタ変数を使用してクラスの一意のインスタンスを指す.
  • は、共通の静的方法を使用してインスタンスを取得する.

  • 単一スレッド内の単一モードの実装:
    class CSingleton
    {
    private:
        CSingleton() //        
        {
        }
        static CSingleton *m_pInstance;
    public:
        static CSingleton * GetInstance()
        {
            if (m_pInstance == NULL) //         
                m_pInstance = new CSingleton();
            return m_pInstance;
        }
    };

    この形式の単一スレッドでは問題ありませんが、マルチスレッドではrace conditionが発生するため、マルチスレッド条件下では、通常、DCL技法、コードは以下の通りです.
    Widget* Widget::pInstance{ nullptr };
    Widget* Widget::Instance() {
        if (pInstance == nullptr) { // 1: first check
            lock_guard lock{ mutW };
            if (pInstance == nullptr) { // 2: second check
                pInstance = new Widget(); 
            }
        } 
        return pInstance;
    }

    しかし、この方法では、コードの3行目のコード:if(pInstance==nullptr)と6行目のコードpInstance=new Widget();正確な同期がない場合、newがpInstance変数にアドレス割り当てを返したのにWidgetが完全に構築されていない場合があります.別のスレッドがその後3行目に実行されるとifに入らず、不完全なインスタンスオブジェクトがユーザーに使用されたことを返し、深刻なエラーが発生します.
    改善点は次のとおりです.
    atomic Widget::pInstance{ nullptr };
    Widget* Widget::Instance() {
        if (pInstance == nullptr) { 
            lock_guard lock{ mutW }; 
            if (pInstance == nullptr) { 
                pInstance = new Widget(); 
            }
        } 
        return pInstance;
    }

    ただし、以下の最も簡潔な方法も使用できます.
     widget& widget::get_instance() {
         static widget instance;
         return instance;
     }