Qt實用技巧:設計模式之單例模式,唯一例項類通用模板
阿新 • • 發佈:2019-01-11
需求
Qt常需要一個類,全域性呼叫,是設計模式中的單例模式。
單例模式
單例模式,是一種常用的軟體設計模式。在它的核心結構中只包含一個被稱為單例的特殊類。通過單例模式可以保證系統中,應用該模式的類一個類只有一個例項。即一個類只有一個物件例項。
顯然單例模式的要點有三個;一是某個類只能有一個例項;二是它必須自行建立這個例項;三是它必須自行向整個系統提供這個例項。
Qt單例模式示例模板(此版本重大bug)
使用DbService::instance()全域性獲取該物件
標頭檔案
#ifndef DBSERVICE_H #define DBSERVICE_H #include <QObject> #include <QMutex> #include <QMutexLocker> class DbService : public QObject { Q_OBJECT public: explicit DbService(QObject *parent = 0); public: static DbService * instance(); signals: public slots: protected: private: static DbService *_pInstance; static QMutex _mutex; }; #endif // DBSERVICE_H
原始檔(存在bug)
#include "DbService.h" DbService * DbService::_pInstance = 0; QMutex DbService::_mutex; DbService::DbService(QObject *parent) : QObject(parent) { } DbService * DbService::instance() { if(!_pInstance) { QMutexLocker lock(&_mutex); if(!_pInstance) { _pInstance = new DbService(); } } return _pInstance; }
bug(感謝網友大神:火龍 的幫助)
_pInstance = new DbService();
1. 申請DbService的記憶體
2. 在申請的記憶體上構造DbService
3. 將_pInstance指標指向這個記憶體
這個new有這麼三步
編譯器可能是132這麼執行的,多個執行緒第一次同時使用時,可能出現野指標,即編譯器先指向記憶體(準備第三步構造)時,另一個執行緒獲取,則出現野指標,執行出現段錯誤。
原始檔(修復完bug)
#include "DbService.h" DbService * DbService::_pInstance = 0; QMutex DbService::_mutex; DbService::DbService(QObject *parent) : QObject(parent) { } DbService * DbService::instance() { if(!_pInstance) { QMutexLocker lock(&_mutex); if(_pInstance) { DbService *pInstance = new DbService(); // 修改處 _pInstance = pInstance; // 修改處 } } return _pInstance; }
Qt單例模式示例模板(修復bug後的單例模式程式碼2:使用原子caozuo)
標頭檔案
#ifndef DBSERVICE_H
#define DBSERVICE_H
#include <QObject>
#include <QMutex>
#include <QMutexLocker>
class DbService : public QObject
{
Q_OBJECT
public:
explicit DbService(QObject *parent = 0);
public:
static DbService &getInstance();
signals:
public slots:
protected:
private:
static QAtomicPointer<DbService> _instance;
static QMutex _mutex;
};
#endif // DBSERVICE_H
原始檔
#include "DbService.h"
QAtomicPointer<DbService> DbService::_instance = 0;
QMutex DbService::_mutex;
DbService::DbService(QObject *parent) : QObject(parent)
{
}
DbService * DbService::instance()
{
#ifndef Q_ATOMIC_POINTER_TEST_AND_SET_IS_ALWAYS_NATIVE
if(!QAtomicPointer::isTestAndSetNative())//執行時檢測
qDebug() << "Error: TestAndSetNative not supported!";
#endif
//使用雙重檢測。
/*! testAndSetOrders操作保證在原子操作前和後的的記憶體訪問
* 不會被重新排序。
*/
if(_instance.testAndSetOrdered(0, 0))//第一次檢測
{
QMutexLocker locker(&mutex);//加互斥鎖。
_instance.testAndSetOrdered(0, new DbService);//第二次檢測。
}
return _instance;
}