1. 程式人生 > >Qt實用技巧:設計模式之單例模式,唯一例項類通用模板

Qt實用技巧:設計模式之單例模式,唯一例項類通用模板

需求

        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;
}