目的
本例簡介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] Efficitive C++