- 爲什麼使用單例模式
大規模系統中,爲了性能的考慮,需要節省對象的創建時間等等。因爲Singleton模式可以保證爲一個類只生成唯一的實例對象,所以這些情況,Singleton模式就派上用場了。比如使用Socket時,只需要一個Socket對象即可,這時候單例模式就有實戰之地了。
實現單例步驟常用步驟
- 構造函數私有化
提供一個全局的靜態方法(全局訪問點)
在類中定義一個靜態指針,指向本類的變量的靜態變量指針
單例模式分類
懶漢式、餓漢式
- 懶漢式單例模式普通寫法 單線程下
Singelton.h #include <iostream> using namespace std; class Singelton { private: Singelton(); public: virtual ~Singelton(); public: static Singelton* getInstance(); void freeInstance(); static void printT(); private: static Singelton *m_Singleton; static int m_count; }; Singelton* Singelton::m_Singleton = nullptr; int Singelton::m_count = 0; ---------------------------------------------------------- Singelton.cpp #include "Singelton.h" Singelton::Singelton() { m_Singleton = nullptr; m_count = 0; } Singelton::~Singelton() { } /**只有當使用的時候纔會將對象創建出來**/ Singelton* Singelton::getInstance() { /*單線程下該寫法沒有問題,多線程有問題*/ if (m_Singleton == nullptr) { m_Singleton = new Singelton(); } return m_Singleton; } void Singelton::freeInstance() { if (m_Singleton != nullptr) { delete m_Singleton; m_Singleton = nullptr; m_count = 0; } } void Singelton::printT() { cout << "m_count: " << m_count << endl; } main.cpp #include"Singelton.h" int main() { Singelton* p1 = Singelton::getInstance(); Singelton* p2 = Singelton::getInstance(); if (p1 != p2) { cout << "不是同一個對象" << endl; } else { cout << "是同一個對象" << endl; } p1->printT(); p2->printT(); system("pause"); Singelton::freeInstance(); return 0; }
- 懶漢式單例模式多線程下寫法
異同點:單線程的寫法使用到多線程後,每次調用GetInstance()靜態方法時,必須判斷 m_Singleton == nullptr,使程序相對開銷增大。在多線程中,該寫法會導致多個實例的產生,從而導致運行代碼不正確以及內存的泄露。
原因:因爲C++中構造函數並不是線程安全的 ,C++中的構造函數簡單來說分兩步:內存分配 、初始化成員變量。由於多線程的關係,可能當我們在分配內存好了以後,還沒來得急初始化成員變量,就進行線程切換,另外一個線程拿到所有權後,由於內存已經分配了,但是變量初始化還沒進行,因此打印成員變量的相關值會發生不一致現象。
但是當使用懶漢式處理大數據時,此處的鎖卻變成了影響性能的關鍵因素。
//多線程優化單例模式 class Singelton { private: Singelton(); //防止拷貝構造和賦值操作 Singelton(const Singelton&) { ; } Singelton& operator=(const Singelton &obj) { ; } public: virtual ~Singelton(); public: static Singelton* getInstance(); static void freeInstance(); void printT(); private: static Singelton *m_Singleton; static std::mutex m_mutex; static int m_count; }; ----------------------------------------------------------- Singelton* Singelton::m_Singleton = nullptr; int Singelton::m_count = 0; std::mutex Singelton::m_mutex; Singelton::Singelton() { m_Singleton = nullptr; m_count = 0; } Singelton::~Singelton() { } /**只有當使用的時候纔會將對象創建出來**/ Singelton* Singelton::getInstance() { if (m_Singleton == nullptr) //double check { //只有當m_Singleton等於null時,纔開始使用加鎖機制 二次檢查 m_mutex.try_lock(); if (m_Singleton == nullptr) { m_Singleton = new Singelton(); } m_mutex.unlock(); } return m_Singleton; } void Singelton::freeInstance() { if (m_Singleton != nullptr) { delete m_Singleton; m_Singleton = nullptr; m_count = 0; } } void Singelton::printT() { std::cout << "m_count: " << m_count << std::endl; }
瞭解了懶漢式單例模式的創建方式後,再來看餓漢式對比度就顯而易見了,,餓漢式就是不管有沒有人要這個實例對象,都先把實例對象創建出來。
//餓漢式單例模式 class Singelton { private: Singelton(); //防止拷貝構造和賦值操作 Singelton(const Singelton&) { ; } Singelton& operator=(const Singelton &obj) { ; } public: virtual ~Singelton(); public: static Singelton* getInstance(); static void freeInstance(); private: static Singelton *m_Singleton; }; Singelton* Singelton::m_Singleton = new Singelton(); Singelton::Singelton() { } Singelton::~Singelton() { } /**只有當使用的時候纔會將對象創建出來**/ Singelton* Singelton::getInstance() { return m_Singleton; } void Singelton::freeInstance() { if (m_Singleton != nullptr) { delete m_Singleton; m_Singleton = nullptr; } }