下面在上一篇的基礎上,咱們進入Qt的源代碼,看看Qt4.x是如何實現 Private Classes 的。
正如前面咱們說的,或許你會看到不少相似 Q_D 或者 Q_Q 這類的宏。那麼,咱們來試着看一下這樣的代碼:ide
- void MyClass::setFoo( int i )
- {
- Q_D(MyClass);
- d->m_foo = i;
- }
- int MyClass::foo() const
- {
- Q_D(const MyClass);
- return d->m_foo;
- }
按照傳統 C++ 的類,若是咱們要實現這樣的 getter 和 setter,咱們應該使用一個私有變量 _i,而後操做這個變量。按照上一篇說的 Private Class 的作法,咱們就要建一個 MyClassPrivateData 這樣的類,而後使用指針對全部的數據操做進行委託。
再來看一個比較 Qt 的例子:函數
- class MyObject: public QObject
- {
- Q_OBJECT
- public:
- MyObject();
- virtual ~ MyObject();
- void setMemberX( int x );
- int memberX() const;
- void setMemberY( double y);
- double memberY() const;
- signals:
- void priorityChanged( MyObject::Priority priority );
- private:
- int m_memberX;
- double m_memberY;
- };
在來看一下 Qt 的實現:spa
- class MyObjectPrivate;
- class MyObject: public QObject
- {
- Q_OBJECT
- public:
- MyObject();
- virtual ~ MyObject();
- void setMemberX( int x );
- int memberX() const;
- void setMemberY( double y);
- double memberY() const;
- signals:
- void priorityChanged( MyObject::Priority priority );
- protected:
- MyObjectPrivate * const d_ptr;
- private:
- Q_DECLARE_PRIVATE(MyObject);
- };
這個例子很簡單,一個使用傳統方法實現,另外一個採用了 Qt4.x 的方法。Qt4.x 的方法被稱爲 D-Pointer,由於它會使用一個名爲 d 的指針,正如上面寫的那個 d_ptr。使用傳統方法,咱們須要在 private 裏面寫上全部的私有變量,一般這會讓整個文件變得很長,更爲重要的是,用戶並不須要這些信息。而使用 D-Pointer 的方法,咱們的接口變得很漂亮:再也沒有那一串長長的私有變量了。你再也不須要將你的私有變量一塊兒發佈出去,它們就在你的 d 指針裏面。若是你要修改數據類型這些信息,你也不須要去修改頭文件,只需改變私有數據類便可。
須要注意的一點是,與單純的 C++ 類不一樣,若是你的私有類須要定義 signals 和 slots,就應該把這個定義放在頭文件中,而不是像上一篇所說的放在 cpp 文件中。這是由於 qmake 只會檢測 .h 文件中的 Q_OBJECT 宏
(這一點你們務必注意)。固然,你不該該把這樣的 private class 放在你的類的同一個頭文件中,由於這樣作的話就沒有意義了。常見作法是,定義一個 private 的頭文件,例如使用 myclass_p.h 的命名方式(這也是 Qt 的命名方式)。而且記住,不要把 private 頭文件放到你發佈的 include 下面!由於這不是你發佈的一部分,它們是私有的。而後,在你的 myclass 頭文件中,使用設計
- class MyClassPrivate;
這種前向聲明而不是直接指針
- #include "myclass_p.h"
這種方式。這也是爲了不將私有的頭文件發佈出去,而且前向聲明能夠縮短編譯時間。
在這個類的 private 部分,咱們使用了一個 MyClassPrivate 的 const 指針 d_ptr。若是你須要讓這個類的子類也可以使用這個指針,就應該把這個 d_ptr 放在 protected 部分,正如上面的代碼那樣。而且,咱們還加上了 const 關鍵字,來確保它只能被初始化一次。
下面,咱們遇到了一個神奇的宏:Q_DECLARE_PRIVATE。這是幹什麼用的?那麼,咱們先來看一下這個宏的展開:對象
- #define Q_DECLARE_PRIVATE(Class) \
- inline Class##Private* d_func() { return reinterpret_cast(qGetPtrHelper(d_ptr)); } \
- inline const Class##Private* d_func() const { return reinterpret_cast(qGetPtrHelper(d_ptr)); } \
- friend class Class##Private;
若是你看不大懂,那麼就用咱們的 Q_DECLARE_PRIVATE(MyClass) 看看展開以後是什麼吧:接口
- inline MyClassPrivate* d_func() { return reinterpret_cast(qGetPtrHelper(d_ptr)); }
- inline const MyClassPrivate* d_func() const { return reinterpret_cast(qGetPtrHelper(d_ptr)); }
- friend class MyClassPrivate;
它實際上建立了兩個 inline 的 d_func() 函數,返回值分別是咱們的 d_ptr 指針和 const 指針。另外,它還把 MyClassPrivate 類聲明爲 MyClass 的 friend。這樣的話,咱們就能夠在 MyClass 這個類裏面使用 Q_D(MyClass) 以及 Q_D(const MyClass)。還記得咱們最早看到的那段代碼嗎?如今咱們來看看這個 Q_D 卻是是何方神聖!get
- // A template function for getting the instance to your private class instance.
- template static inline T *qGetPtrHelper(T *ptr) { return ptr; }
- // A macro for getting the d-pointer
- #define Q_D(Class) Class##Private * const d = d_func()
下面仍是本身展開一下這個宏,就成了it
- MyClassPrivate * const d = d_func()
簡單來講,Qt 爲咱們把從 d_func() 獲取 MyClassPrivate 指針的代碼給封裝起來了,這樣咱們就能夠比較面向對象的使用 getter 函數獲取這個指針了。io
如今咱們已經比較清楚的知道 Qt 是如何使用 D-Pointer 實現咱們前面所說的信息隱藏的了。可是,還有一個問題:若是咱們把大部分代碼集中到 MyClassPrivate 裏面,極可能須要讓 MyClassPrivate 的實現訪問到 MyClass 的一些東西。如今咱們讓主類經過 D-Pointer 訪問 MyClassPrivate 的數據,可是怎麼反過來讓 MyClassPrivate 訪問主類的數據呢?Qt 也提供了相應的解決方案,那就是 Q_Q 宏,例如:
- class MyObjectPrivate
- {
- public:
- MyObjectPrivate(MyObject * parent):
- q_ptr( parent ),
- m_priority(MyObject::Low)
- {}
- void foo()
- {
- // Demonstrate how to make MyObject to emit a signal
- Q_Q(MyObject);
- emit q->priorityChanged( m_priority );
- }
- // Members
- MyObject * const q_ptr;
- Q_DECLARE_PUBLIC(MyObject);
- MyObject::Priority m_priority;
- };
在 private 類 MyObjectPrivate 中,經過構造函數將主類 MyObject 的指針傳給 q_ptr。而後咱們使用相似主類中使用的 Q_DECLARE_PRIVATE 的宏同樣的另外的宏 Q_DECLARE_PUBLIC。這個宏所作的就是讓你可以經過 Q_Q(Class) 宏使用主類指針。與 D-Pointer 不一樣,這時候你須要使用的是 Q_Pointer。這兩個是徹底相對的,這裏也就再也不贅述。
如今咱們已經可以使用比較 Qt 的方式來使用 Private Classes 實現信息隱藏了。這不只僅是 Qt 的實現,固然,你也能夠不用 Q_D 和 Q_Q,而是使用本身的方式,這些都可有可無。最主要的是,咱們瞭解了一種 C++ 類的設計思路,這是 Qt 的源代碼教給咱們的。