c++ 單例模式的一點疑問,求解答?
在《大話設計模式》和一些博客上都看到過一些單例模式的實現,為什麼他們不自定義拷貝構造函數,下面我給出了一個例子,我覺得是應該自定義拷貝構造函數的。另外,需不需要定義class DeleteSingleton用於刪除共享的單例?
#include&
using namespace std;
class Singleton{
private :
int a;
static Singleton *instance;
protected:
Singleton(int v):a(v){ }
//Singleton(const Singleton ins){//複製構造函數
// a=ins.a;
//}
/*class DeleteSingleton{//用於刪除instance
~DeleteSingleton(){
if(Singleton::instance){
delete Singleton::instance;
}
}
};
static DeleteSingleton DelSgl;*/
public:
static Singleton* GetInstance(){
if(instance==NULL){
instance=new Singleton(4);
}
return instance;
}
};
Singleton* Singleton::instance=NULL;
int main(){
Singleton *p=Singleton::GetInstance();
Singleton *p2=Singleton::GetInstance();
cout&<&<(p==p2)&<&
Singleton ins(*p);//這裡如何沒有自定義私有(或protected)拷貝構造函數是可以正常運行的
Singleton *p3=ins;
cout&<&<(p==p3)&<&
system("pause");
return 0;
}
1、其實應該把複製構造函數聲明為=delete,反正不需要複製。
2、DeleteSingleton也是多餘的,畢竟main函數結束以後才delete,跟不delete是沒有任何區別的,進程都要被幹掉了,你還操這個心幹什麼。通常delete都是為了讓內存泄漏檢查演算法不要誤報,從而在main函數裡面手動釋放。
構造析構全放private,拷貝和賦值delete,單例只需要兩行代碼。
static Singleton getInstance()
{ static Singleton instance; return instance;}採用c++11及以上版本編譯,線程安全,lazy-initialize,自動銷毀,完美。不要用單例這種anti-pattern就行了。
既然是單例了,自然不允許拷貝和轉移啦。講道理是應該delete掉的。
給你看個sample吧https://github.com/atframework/atframe_utils/blob/master/include/design_pattern/singleton.h
除了你說的delete問題外還有些訪問許可權控制的問題說多一點:我認為單例實現成什麼樣子,完全取決於具體需要,根本不用那麼教條。比如:
1:業務邏輯是否真的需要(儘可能)嚴格限制全局僅有一個實例?
有些時候業務邏輯並沒有這種限制,那麼連講構造函數設為protected/private都是不需要的。比如隨機數生成器:我提供一個getInstance靜態方法,讓你在要求不高的時候,直接使用全局的實例,避免反覆創建、持有大量PRNG實例帶來的代價;同時也允許你另外構造、持有自己的PRNG實例,以便你在需要特定的隨機數序列的時候,能夠嚴格控制PRNG初始狀態與後續序列。
2:如果1無所謂,不需要嚴格限制全局唯一實例,那麼是否需要限制複製?
如果這個類在業務邏輯上可以複製,那就讓用戶複製唄。
3:是否需要一個人工釋放單例的方法?是否需要多線程訪問?
如果不需要,完全可以把單例簡化成函數包裝的靜態變數:
MyType get_instance()
{
static MyType instance;
return instance;
}
保證在第一次訪問時創建,在程序退出時析構,簡單粗暴。就是不能提前析構。
單例的目的是確保程序處處使用的是一個對象,順帶使用者不需要關心該對象的創建釋放,只有一個也不該複製。一般來說,單例模式和要做成單例的類可以可以分開。不應該給使用者留刪除介面,單例對象基本只該在程序退出時刪除:靜態對象生命期結束調用其析構函數,或者手動用atexit註冊
單例模式主要注意兩點:
1 線程安全,及針對的優化技巧:double check
if(p == nullptr){
scopeguard g(mutex_);
if(p == nullptr)
p = new T;
}
2 單例對象間的依賴順序以及釋放順序
這個比較複雜,大規模C++程序設計 和 《C++設計新思維》里講解比較詳細,直接上github上搜loki看singleton,要確保析構順序就需要自定義的析構所有單例的函數,將其放入atexit來完成。
當然這種代碼很容易封裝的,所以別害怕,你不用每次都寫,把loki的實現摘出來存好就是了。
推薦閱讀:
※在替考的代理模式中到底誰是Proxy?
※怎樣才能在寫代碼時沒有一種「如履薄冰」的感覺?
※用 C++ 編程時,如果不使用設計模式,多層封裝,採用複雜的數據結構,代碼更直觀,易理解,引入(設計模式)後雖然做到了高度的解耦。但是代碼邏輯複雜了,怎麼平衡好?
※有哪些在實際 Android 項目中用到的設計模式?
※Android 開發中常用到的設計模式有哪些?