天天看點

Qt核心剖析:資訊隐藏(3)

前面我們已經看到了怎樣使用标準的 C++ 代碼以及 Qt 提供的 API 來達到資訊隐藏這一目标。下面我們來看一下 Qt 是如何實作的。

還是以 QObject 的源代碼作為例子。先打開 qobject.h,找到 QObjectData 這個類的聲明。具體代碼如下所示:

然後在下面就可以找到 QObject 的聲明:

注意,這裡我們隻是列出了我們所需要的代碼,并且我的 Qt 版本是 2010.03。這部分代碼可能會随着不同的 Qt 版本所有不同。

首先先了解一下 Qt 的設計思路。既然每個類都應該把自己的資料放在一個 private 類中,那麼,為什麼不把這個操作放在幾乎所有類的父類 QObject 中呢?是以,Qt 實際上是用了這樣一個思路,實作了我們前面介紹的資料隐藏機制。

首先回憶一下,我們前面說的 D-Pointer 需要有一個 private 或者 protected 的指向自己資料類的指針。在 QObject 中,

就扮演了這麼一個角色。或許,你可以把它了解成

這不就和我們前面說的 D-Pointer 技術差不多了?QScopedPointer 是 Qt 提供的一個輔助類,這個類儲存有一個指針,它的行為類似于一種智能指針:它能夠保證在這個作用域結束後,裡面的所有指針都能夠被自動 delete 掉。也就是說,它其實就是一個比普通指針更多功能的指針。而這個指針被聲明成 protected 的,也就是隻有它本身以及其子類才能夠通路到它。這就提供了讓子類不必須聲明這個 D-Pointer 的可能。

那麼,前面我們說,QObjectData 這種資料類不應該放在公開的頭檔案中,可 Qt 怎麼把它放進來了呢?這樣做的用途是,QObject 的子類的資料類都可能繼承自這個 QObjectData。這個類有一個純虛的析構函數。沒有實作代碼,保證了這個類不能被初始化;虛的析構函數,保證了其子類都能夠被正确的析構。

回到我們前面說明的 Q_DECLARE_PRIVATE 這個宏:

我們把代碼中的 Q_DECLARE_PRIVATE(QObject) 展開看看是什麼東西:

清楚是清楚,隻是這個 QObjectPrivate 是哪裡來的?既然是 Private,那麼它肯定不會在公開的頭檔案中。于是我們立刻想到到 qobject.cpp 或者是 qobject_p.h 中尋找。終于,我們在 qobject_p.h 中找到了這個類的聲明:

這個類是繼承 QObjectData 的!想想也是,因為我們說過,QObjectData 是不能被執行個體化的,如果要使用,必須建立它的一個子類。顯然,QObjectPrivate 就扮演了這麼一個角色了。不僅如此,我們還在這裡看到了熟悉的 Q_DECLARE_PUBLIC 宏。好在我們已經知道它的含義了。

在 qobject.cpp 中,我們看一下 QObject 的構造函數:

第一個構造函數就是我們經常見到的那個。它使用自己建立的 QObjectPrivate 指針對 d_ptr 初始化。第二個構造函數使用傳入的 QObjectPrivate 對象,但它是 protected 的,也就是說,你不能在外部類中使用這個構造函數。那麼這個構造函數有什麼用呢?我們來看一下 QWidget 的代碼:

QWidget 是 QObject 的子類,然後看它的構造函數:

它調用了那個QObject 的 protected 構造函數,并且傳入一個 QWidgetPrivate !這個 QWidgetPrivate 顯然繼承了 QObjectPrivate。于是我們已經明白,為什麼 QWidget 中找不到 d_ptr 了,因為所有的 d_ptr 都已經在父類 QObject 中定義好了!嘗試展開一下 Q_DECLARE_PRIVATE 宏,你就能夠發現,它實際上把父類的 QObjectPrivate 指針偷偷地轉換成了 QWidgetPrivate 的指針。這個就是前面說的 Qt 的設計思路。

繼續閱讀