c++: 單例模式(Singleton)的最優寫法

目的

本例簡介C++中單例模式的最優寫法。

實現

基礎寫法

下面的代碼是C++單例的基礎寫法,在靜態函數Singleton::getInstance()中定義了Singleton的靜態變量對象,並返回此對象的引用。
由於C++函數的靜態變量唯一性,可以確保例子中s對象的唯一性,線程同步,以及靜態對象間的依賴關係等問題。

#include <iostream>

class Singleton {
   
   
    public: static Singleton &getInstance() {
   
   
        static Singleton s;
        return s;
    }

    public: void test() {
   
   
        std::cout << "test" << std::endl;
    }
};

int main() {
   
   
    Singleton &s = Singleton::getInstance();
    s.test();
    return 0;
}

完整寫法

如果對代碼要求比較嚴格,可以把該關閉的函數都關掉,這取決與你:):

  • 構造函數私有化,使得外部無法創建Singleton對象。
  • 關閉拷貝構造函數,右值拷貝構造函數。
  • 關閉賦值運算符重載函數。
class Singleton {
   
   
    private: Singleton() {
   
    }
    Singleton(const Singleton &) = delete;
    Singleton(const Singleton &&) = delete;
    Singleton &operator=(const Singleton &) = delete;

    public: static Singleton &getInstance() {
   
   
        static Singleton s;
        return s;
    }

    public: void test() {
   
   
        std::cout << "test" << std::endl;
    }
};

對比

New

使用下面方法的也比較多,缺點是在無法保證getInstance()的線程安全性。如果工程比較大,會存在多個線程同時調用Singleton::getInstance()方法導致創建多實例的問題。

class Singleton {
   
   
    private: static Singleton *instance;

    public: static Singleton *getInstance() {
   
   
        if (NULL == instance)
            instance = new Singleton();

        return instance;
    }
};

Lock

所以可能會加一堆lock, 爲了性能還寫個double check, 比如下面這樣,寫起來比較麻煩,個人不太喜歡。

class Singleton {
   
   
    private: static Singleton *instance;

    public: static Singleton *getInstance() {
   
   
        if (NULL == instance) {
   
   
            // TODO LOCK
            if (NULL == instance)
                instance = new Singleton();
        }

        return instance;
    }
};

靜態成員

如果把函數內的靜態變量變成類的靜態成員變量呢?簡單的工程行,複雜的不行。因爲如果靜態類之間有依賴,可能會導致C++的一些未定義的行爲。

  • 下例中的Singleton::instance 保存在程序全局的靜態數據區,instance初始化的時機是在程序的main()函數執行前。
  • 假設有SingletonB::instance,與Singleton::instance類似定義,也是靜態類成員變量。SingletonB::instance和Singleton::instance的初始化順序是未定義的,得看編譯器的心情。
  • 如果Singleton::instance 的初始化在SingletonB::instance之前,而Singleton的構造函數中恰好需要引用到SIngleonB::instance,就很可能會出現一些未定義的行爲。
#include <iostream>

class Singleton {
   
   
    private: static Singleton instance;

    public: static Singleton &getInstance() {
   
   
        return instance;
    }
    
    public: void test() {
   
   
        std::cout << "test" << std::endl;
    }
};

Singleton Singleton::instance;

int main() {
   
   
    Singleton &s = Singleton::getInstance();
    s.test();
    return 0;
}

總結

  1. 如果項目小,建議上述單例中的簡單寫法就夠了。
  2. 如果項目大,可以寫全點兒。

引用

[1] Efficitive C++

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章