Qt-单例模式实例
單例模式
單利模式作為一種常用的軟件設計模式,主要是用來保證系統中只有一個實例,例如一般一個程序中只有一個日志輸出實例,一個系統中只有一個數據庫連接實例,這時候用單例模式非常合適。
簡單的單例模式
1 class QSingleton2 {3 public:4 static QSingleton* instance()5 {6 if (m_pInstance == NULL)7 {8 m_pInstance = new QSingleton();9 } 10 return m_pInstance; 11 } 12 13 static void Release() 14 { 15 if (m_pInstance != NULL) 16 { 17 delete m_pInstance; 18 m_pInstance = NULL; 19 } 20 } 21 private: 22 QSingleton(){} 23 QSingleton(const QSingleton&){} 24 QSingleton& operator==(const QSingleton&){} 25 private: 26 static QSingleton* m_pInstance; 27 }; 28 // 靜態成員變量需要在類體的外面進行初始化 29 QSingleton* QSingleton::m_pInstance = NULL;上面的實現一種最簡單的單利模式,是一種懶漢模式,所謂的懶漢模式就是在程序需要時才進行成員變量的創建也就是“延時加載”,與之相對的就是餓漢模式,惡漢模式就是在程序啟動時就需要創建變量。懶漢模式是時間換空間,惡漢模式是空間換時間,看如下惡漢模式的一個簡單實現:
1 class QSingleton2 {3 public:4 static QSingleton* instance()5 {6 return m_pInstance;7 }8 9 static void Release() 10 { 11 if (m_pInstance != NULL) 12 { 13 delete m_pInstance; 14 m_pInstance = NULL; 15 } 16 } 17 QSingleton(){} 18 19 private: 20 QSingleton(const QSingleton&){} 21 QSingleton& operator==(const QSingleton&){} 22 private: 23 static QSingleton* m_pInstance; 24 }; 25 26 // 直接初始化靜態成員變量 27 QSingleton* QSingleton::m_pInstance = new QSingleton;因為程序啟動時,就需要創建對象,所以單例類的默認構造函數就需要時public的,此時用戶就能夠創建單例類的對象,從而就不能保證單例模式的初衷:一個程序只有一個實例類,另外當我們的單例類的默認構造函數需要參數時,并且改參數需要在程序執行過程中才能夠構造,此時就不能用餓漢模式的單例模式。因此下面著重對懶漢模式的單例模式實現做討論。上面的簡單的懶漢模式的單例類實現有如下缺點:
每次都得判斷m_pInstance是否為空,增加了程序開銷,而餓漢模式沒有此問題。
需要手動調用Release函數釋放靜態成員變量分配內存,上面的餓漢模式也有此問題。針對此問題我們可以通過智能指針來避免。
不是線程安全的,要想在多線程環境下安全使用,就需要在程序一開始處,其他線程還未創建時,調用一次instance函數,但這樣就拋棄了懶漢模式延遲加載的優點。餓漢模式因為在程序一開始就創建了對象,因此是線程安全的。
線程安全的單例模式
通過智能指針來管理成員變量,保證了在程序退出時,自動釋放內存,通過加鎖保證了m_pInstance創建的唯一性,但是因為程序每次調用instance就需要先加鎖,大大增加了程序開銷,看如下改進實現:
1 class QSingleton2 {3 public:4 static QSharedPointer<QSingleton>& instance()5 {6 7 if (m_pInstance.isNull())8 {9 QMutexLocker mutexLocker(&m_Mutex); 10 if (m_pInstance.isNull()) 11 m_pInstance = QSharedPointer<QSingleton>(new QSingleton()); 12 } 13 return m_pInstance; 14 } 15 private: 16 QSingleton(){} 17 QSingleton(const QSingleton&){} 18 QSingleton& operator==(const QSingleton&){} 19 private: 20 static QMutex m_Mutex; 21 static QSharedPointer<QSingleton> m_pInstance; 22 }; 23 24 QMutex QSingleton::m_Mutex; 25 QSharedPointer<QSingleton> QSingleton::m_pInstance;上面的實現通過兩次檢查成員變量是否為空(double-check),避免了每次調用instance函數就鎖定的效率問題。
Meyers提出的一種單例模式的實現
1 class QSingleton2 {3 public:4 static QSingleton& instance()5 {6 static QSingleton qinstance;7 return qinstance;8 }9 private: 10 QSingleton(){} 11 QSingleton(const QSingleton&){} 12 QSingleton& operator==(const QSingleton&){} 13 };在上述單例模式的實現中,在instance函數中聲明static的局部變量,因為靜態變量在程序中只會分配一次內存,保證了實例的唯一性,并且作為局部變量只有在程序第一次調用的時候才會初始化,也實現了延遲加載,而且因為不是指針變量,在程序結束時會自動回收內存,幾乎就是完美的實現。雖然是只分配一次內存,但就能夠確保線程安全嗎?答案是否定的,因為c++的構造函數本身就不是線程安全的,當我們在構造函數內部初始化成員變量或者全局變量時,時間片就有可能被切走,我們在使用時,這一點尤為重要。
總結
- 上一篇: Halcon 3D点云和深度图的相互转化
- 下一篇: 2015Astar百度之星初赛 1005