Qt核心剖析:信息隱藏(2)

下面在上一篇的基礎上,咱們進入Qt的源代碼,看看Qt4.x是如何實現 Private Classes 的。

正如前面咱們說的,或許你會看到不少相似 Q_D 或者 Q_Q 這類的宏。那麼,咱們來試着看一下這樣的代碼:ide

  
  
           
  
  
  1. void MyClass::setFoo( int i ) 
  2.     Q_D(MyClass); 
  3.     d->m_foo = i; 
  4.  
  5. int MyClass::foo() const 
  6.     Q_D(const MyClass); 
  7.     return d->m_foo; 

按照傳統 C++ 的類,若是咱們要實現這樣的 getter 和 setter,咱們應該使用一個私有變量 _i,而後操做這個變量。按照上一篇說的 Private Class 的作法,咱們就要建一個 MyClassPrivateData 這樣的類,而後使用指針對全部的數據操做進行委託。

再來看一個比較 Qt 的例子:函數

  
  
           
  
  
  1. class MyObject: public QObject   
  2. {   
  3.     Q_OBJECT   
  4.    
  5. public:   
  6.     MyObject();   
  7.     virtual ~ MyObject();   
  8.     void setMemberX( int x );   
  9.     int memberX() const;   
  10.     void setMemberY( double y);   
  11.     double memberY() const;   
  12.          
  13. signals:   
  14.     void priorityChanged( MyObject::Priority priority );   
  15.          
  16. private:   
  17.     int    m_memberX;   
  18.     double m_memberY;  
  19. }; 

在來看一下 Qt 的實現:spa

  
  
           
  
  
  1. class MyObjectPrivate;   
  2. class MyObject: public QObject   
  3. {   
  4.     Q_OBJECT   
  5.  
  6. public:   
  7.     MyObject();   
  8.     virtual ~ MyObject();   
  9.     void setMemberX( int x );   
  10.     int memberX() const;   
  11.     void setMemberY( double y);   
  12.     double memberY() const;   
  13.  
  14. signals:   
  15.     void priorityChanged( MyObject::Priority priority );   
  16.  
  17. protected:   
  18.     MyObjectPrivate * const d_ptr;   
  19.  
  20. private:   
  21.     Q_DECLARE_PRIVATE(MyObject);   
  22. };   

這個例子很簡單,一個使用傳統方法實現,另外一個採用了 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 頭文件中,使用設計

  
  
           
  
  
  1. class MyClassPrivate; 

這種前向聲明而不是直接指針

  
  
           
  
  
  1. #include "myclass_p.h" 

這種方式。這也是爲了不將私有的頭文件發佈出去,而且前向聲明能夠縮短編譯時間。

在這個類的 private 部分,咱們使用了一個 MyClassPrivate 的 const 指針 d_ptr。若是你須要讓這個類的子類也可以使用這個指針,就應該把這個 d_ptr 放在 protected 部分,正如上面的代碼那樣。而且,咱們還加上了 const 關鍵字,來確保它只能被初始化一次。

下面,咱們遇到了一個神奇的宏:Q_DECLARE_PRIVATE。這是幹什麼用的?那麼,咱們先來看一下這個宏的展開:對象

  
  
           
  
  
  1. #define Q_DECLARE_PRIVATE(Class) \   
  2.     inline Class##Private* d_func() { return reinterpret_cast(qGetPtrHelper(d_ptr)); } \   
  3.     inline const Class##Private* d_func() const { return reinterpret_cast(qGetPtrHelper(d_ptr)); } \   
  4.     friend class Class##Private;   

若是你看不大懂,那麼就用咱們的 Q_DECLARE_PRIVATE(MyClass) 看看展開以後是什麼吧:接口

  
  
           
  
  
  1. inline MyClassPrivate* d_func() { return reinterpret_cast(qGetPtrHelper(d_ptr)); }   
  2. inline const MyClassPrivate* d_func() const { return reinterpret_cast(qGetPtrHelper(d_ptr)); }   
  3. friend class MyClassPrivate;   

它實際上建立了兩個 inline 的 d_func() 函數,返回值分別是咱們的 d_ptr 指針和 const 指針。另外,它還把 MyClassPrivate 類聲明爲 MyClass 的 friend。這樣的話,咱們就能夠在 MyClass 這個類裏面使用 Q_D(MyClass) 以及 Q_D(const MyClass)。還記得咱們最早看到的那段代碼嗎?如今咱們來看看這個 Q_D 卻是是何方神聖!get

  
  
           
  
  
  1. // A template function for getting the instance to your private class instance.   
  2. template  static inline T *qGetPtrHelper(T *ptr) { return ptr; }   
  3.  
  4. // A macro for getting the d-pointer   
  5. #define Q_D(Class) Class##Private * const d = d_func()   

下面仍是本身展開一下這個宏,就成了it

  
  
           
  
  
  1. MyClassPrivate * const d = d_func() 

簡單來講,Qt 爲咱們把從 d_func() 獲取 MyClassPrivate 指針的代碼給封裝起來了,這樣咱們就能夠比較面向對象的使用 getter 函數獲取這個指針了。io

如今咱們已經比較清楚的知道 Qt 是如何使用 D-Pointer 實現咱們前面所說的信息隱藏的了。可是,還有一個問題:若是咱們把大部分代碼集中到 MyClassPrivate 裏面,極可能須要讓 MyClassPrivate 的實現訪問到 MyClass 的一些東西。如今咱們讓主類經過 D-Pointer 訪問 MyClassPrivate 的數據,可是怎麼反過來讓 MyClassPrivate 訪問主類的數據呢?Qt 也提供了相應的解決方案,那就是 Q_Q 宏,例如:

  
  
           
  
  
  1. class MyObjectPrivate   
  2. {   
  3. public:   
  4.     MyObjectPrivate(MyObject * parent):   
  5.             q_ptr( parent ),   
  6.             m_priority(MyObject::Low)   
  7.     {}   
  8.     void foo()   
  9.     {   
  10.        // Demonstrate how to make MyObject to emit a signal   
  11.        Q_Q(MyObject);   
  12.        emit q->priorityChanged( m_priority );   
  13.     }   
  14.    
  15.     //  Members   
  16.     MyObject * const q_ptr;   
  17.     Q_DECLARE_PUBLIC(MyObject);   
  18.     MyObject::Priority m_priority;   
  19. };   

在 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 的源代碼教給咱們的。

相關文章
相關標籤/搜索