您好,登錄后才能下訂單哦!
什么是設計模式
設計模式代表了最佳實踐,是軟件開發過程中面臨一般問題的解決方案。
設計模式是一套被反復使用、經過分類、代碼設計總結的經驗。
單例模式
單例模式也叫單件模式。Singleton是一個非常常用的設計模式,幾乎所有稍微大一些的程序都會使用到它,所以構建一個線程安全并且高效的Singleton很重要。
1. 單例類保證全局只有一個唯一實例對象。
2. 單例類提供獲取這個唯一實例的接口。
怎樣設計一個單例模式
實現一(不考慮線程安全)
class Singleton { public: // 獲取唯一對象實例的接口函數 static Singleton* GetInstance() { if(_sInstance == NUL) { if (_sInstance == NULL) { _sInstance = new Singleton(); } } return _sInstance; } // 刪除實例對象 static void DelInstance() { if (_sInstance) { delete _sInstance; _sInstance = NULL; } } void Print() { cout<<_data<<endl; } private: // 構造函數定義為私有,限制只能在類內創建對象 Singleton() :_data(0) {} Singleton(const Singleton&); Singleton& operator=(const Singleton&); // 指向實例的指針定義為靜態私有,這樣定義靜態成員函數獲取對象實例 static Singleton* _sInstance; // 單例類里面的數據 int _data; }; Singleton* Singleton::_sInstance = NULL; void TestSingleton() { Singleton::GetInstance()->Print(); Singleton::DelInstance(); }
實現二
線程安全的單例 -- (懶漢模式-- lazy loading)
ps: 下面部分的加鎖使用了C++11庫的互斥鎖 class Singleton { public: // 獲取唯一對象實例的接口函數 static Singleton* GetInstance() { // 使用雙重檢查,提高效率,避免高并發場景下每次獲取實例對象都進行加鎖 if (_sInstance == NULL) { std::lock_guard<std::mutex> lck(_mtx); if (_sInstance == NULL) { // tmp = new Singleton()分為以下三個部分 // 1.分配空間 2.調用構造函數 3.賦值 // 編譯器編譯優化可能會把2和3進行指令重排,這樣可能會導致 // 高并發場景下,其他線程獲取到未調用構造函數初始化的對象 // 以下加入內存柵欄進行處理,防止編譯器重排柵欄后面的賦值 // 到內存柵欄之前 Singleton* tmp = new Singleton(); MemoryBarrier(); _sInstance = tmp; } } return _sInstance; } // 刪除實例對象 static void DelInstance() { std::lock_guard<std::mutex> lck(_mtx); if (_sInstance) { delete _sInstance; _sInstance = NULL; } } void Print() { cout<<_data<<endl; } private: // 構造函數定義為私有,限制只能在類內創建對象 Singleton() :_data(0) {} Singleton(const Singleton&); Singleton& operator=(const Singleton&); // 指向實例的指針定義為靜態私有,這樣定義靜態成員函數獲取對象實例 static Singleton* _sInstance; // 保證線程安全的互斥鎖 static mutex _mtx; // 單例類里面的數據 int _data; }; Singleton* Singleton::_sInstance = NULL; mutex Singleton::_mtx; void TestSingleton() { Singleton::GetInstance()->Print(); Singleton::DelInstance(); }
實現三
線程安全的單例 -- (餓漢模式--簡潔、高效、不用加鎖、但是在某些場景下會有缺陷)
方法1
// 方式一 class Singleton { public: // 獲取唯一對象實例的接口函數 static Singleton* GetInstance() { static Singleton sInstance; return &sInstance; } void Print() { cout<<_data<<endl; } private: // 構造函數定義為私有,限制只能在類內創建對象 Singleton() :_data(0) {} Singleton(const Singleton&); Singleton& operator=(const Singleton&); // 單例類里面的數據 int _data; }; void TestSingleton() { Singleton::GetInstance()->Print(); }
方法2
// 方式二 class Singleton { public: // 獲取唯一對象實例的接口函數 static Singleton* GetInstance() { assert(_sInstance); return _sInstance; } // 刪除實例對象 static void DelInstance() { if (_sInstance) { delete _sInstance; _sInstance = NULL; } } void Print() { cout << _data << endl; } private: // 構造函數定義為私有,限制只能在類內創建對象 Singleton() :_data(0) {} Singleton(const Singleton&); Singleton& operator=(const Singleton&); // 指向實例的指針定義為靜態私有,這樣定義靜態成員函數獲取對象實例 static Singleton* _sInstance; // 單例類里面的數據 int _data; }; Singleton* Singleton::_sInstance = new Singleton; void TestSingleton() { Singleton::GetInstance()->Print(); Singleton::DelInstance(); }
帶RAII GC 自動回收實例對象的方式
class Singleton { public: // 獲取唯一對象實例的接口函數 static Singleton* GetInstance() { assert(_sInstance); return _sInstance; } // 刪除實例對象 static void DelInstance() { if (_sInstance) { delete _sInstance; _sInstance = NULL; } } void Print() { cout << _data << endl; } class GC { public: ~GC() { cout << "DelInstance()"<<endl; DelInstance(); } }; private: // 構造函數定義為私有,限制只能在類內創建對象 Singleton() :_data(0) {} // 指向實例的指針定義為靜態私有,這樣定義靜態成員函數獲取對象實例 static Singleton* _sInstance; // 單例類里面的數據 int _data; }; // 靜態對象在main函數之前初始化,這時只有主線程運行,所以是線程安全的。 Singleton* Singleton::_sInstance = new Singleton; // 使用RAII,定義全局的GC對象釋放對象實例 Singleton::GC gc; void TestSingleton() { Singleton::GetInstance()->Print(); }
由于程序在結束的時候,系統會自動析構所有的全局變量,實際上,系統也會析構所有類的靜態成員變量,就像這些靜態變量是全局變量一樣。我們知道,靜態變量和全局變量在內存中,都是存儲在靜態存儲區的,所以在析構時,是同等對待的。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。