1. 程式人生 > >單例的智慧指標實現

單例的智慧指標實現

前面提出了一個問題:可不可以不需要我們手動的呼叫 release() 函式,程式結束前自動的刪除單例類的物件呢?答案是可以,使用智慧指標可以達到這個目的,這裡我們使用的是 Qt 的 QScopedPointer 來實現,也可以使用標準的 C++ 的智慧指標。

Qt 的幫助文件裡對 QScopedPointer 的描述是

The QScopedPointer class stores a pointer to a dynamically allocated object, and deletes it upon destruction.

也就是說,QScopedPointer 持有一個指標,當 QScopedPointer 變數超出它的作用域後,就會自動的呼叫 delete 刪除它持有的指標物件。我們在單例類 ConfigUtil 裡定義了一個靜態的 QScopedPointer 的變數 instance,其持有 ConfigUtil 類的指標物件,程式結束的時候會自動析構 instance 而析構單例類的指標物件,所以就不需要在程式結束的時候手動的呼叫 release() 函數了。

關於 QScopedPointer 更多的資訊請參考 Qt 的幫助文件,裡面有很多例子,對理解 QScopedPointer 很有幫助,可以如下圖在 QtCreator 裡搜尋: 

ConfigUtil.h

#ifndef CONFIGUTIL_H
#define CONFIGUTIL_H

#include <QString>
#include <QMutex>
#include <QScopedPointer>

class ConfigUtil {
public:
    QString getDatabaseName() const;

    static
ConfigUtil& getInstance(); static void release(); private: ConfigUtil(); ~ConfigUtil(); ConfigUtil(const ConfigUtil &other); ConfigUtil& operator=(const ConfigUtil &other); static QMutex mutex; static QScopedPointer<ConfigUtil> instance; friend
struct QScopedPointerDeleter<ConfigUtil>; }; #endif // CONFIGUTIL_H

static QScopedPointer<ConfigUtil> instance 建立的靜態變數 instance 會在程式結束前析構(在main() 函式返回之後),呼叫 delete 刪除它持有的 ConfigUtil 的指標物件。

也許你會覺得有點奇怪的是 friend struct QScopedPointerDeleter<ConfigUtil>,為什麼是宣告 QScopedPointerDeleter 為 friend struct,而不是宣告 QScopedPointer 為 friend class?

這和 QScopedPointer 的實現有關,QScopedPointer 被析構時不是直接在它的解構函式裡呼叫 delete 刪除它持有的指標物件,而是使用它的 Cleanup Handler 來刪除,預設是 QScopedPointerDeleter,在 QScopedPointer 的幫助裡有具體的說明:

Custom Cleanup Handlers

Arrays as well as pointers that have been allocated with malloc must not be deleted using delete. QScopedPointer's second template parameter can be used for custom cleanup handlers.

The following custom cleanup handlers exist:

  • QScopedPointerDeleter - the default, deletes the pointer using delete
  • QScopedPointerArrayDeleter - deletes the pointer using delete []. Use this handler for pointers that were allocated with new [].
  • QScopedPointerPodDeleter - deletes the pointer using free(). Use this handler for pointers that were allocated with malloc().
  • QScopedPointerDeleteLater - deletes a pointer by calling deleteLater() on it. Use this handler for pointers to QObject's that are actively participating in a QEventLoop.

You can pass your own classes as handlers, provided that they have a public static function void cleanup(T *pointer).

ConfigUtil.cpp

#include "ConfigUtil.h"
#include <QDebug>

QMutex ConfigUtil::mutex;
QScopedPointer<ConfigUtil> ConfigUtil::instance;

ConfigUtil::ConfigUtil() {
    qDebug() << "ConfigUtil()";
}

ConfigUtil::~ConfigUtil() {
    qDebug() << "~ConfigUtil()";
}

QString ConfigUtil::getDatabaseName() const {
    return "Verbose";
}

ConfigUtil& ConfigUtil::getInstance() {
    if (instance.isNull()) {
        mutex.lock();
        if (instance.isNull()) {
            instance.reset(new ConfigUtil());
        }
        mutex.unlock();
    }

    return *instance.data();
}

main.cpp

main.cpp 用於展示 ConfigUtil 的使用。

#include "ConfigUtil.h"
#include <QDebug>

int main(int argc, char *argv[]) {
    Q_UNUSED(argc)
    Q_UNUSED(argv)

    qDebug() << ConfigUtil::getInstance().getDatabaseName();
    qDebug() << ConfigUtil::getInstance().getDatabaseName();

    return 0;
}

輸出:

ConfigUtil()
"Verbose"
"Verbose"
~ConfigUtil()

不需要像前面那樣手動呼叫 release(),ConfigUtil 被自動析構了。

思考

從 ConfigUtil 的實現,我們知道了怎麼寫一個單例類,現在需要另一個單例的類如 ConnectionPool(請自己實現,相關的功能函式可以隨便返回點東西表示一下就可以了),比較一下它們的程式碼是否有什麼相似部分,是不是單例的實現部分都要抄一遍 ConfigUtil 的相關程式碼?

轉載:http://qtdebug.com/Singleton-2-AutoPointer.html