Qt中的Q_D宏和d指針

1 _ZTS7QObject

 

1、Q_D的在文件中的提法node

  Q_D的設置意在方便地獲取私有類指針,文件爲qglobal.h。下面的##是宏定義的連字符。假設類名是A,那麼A##Private翻譯過來就是APrivate。c++

1 #define Q_D(Class) Class##Private * const d = d_func()

  d_func()函數以下實現:windows

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

  這裏的d_func()雖然在宏裏面,可是若是代入具體的類型,這裏面就變成了以Class##Private的私有類指針爲返回值,以func爲函數名的函數。這裏的qGetPtrHelper是函數

1 template <typename T> static inline T *qGetPtrHelper(T *ptr) { return ptr; }

  這個模板函數裏面裏的T要套入某個具體的類。上面的那個Q_DECLARE_PRIVATE裏面的就成了這個模板類的一個調用。這個模板類就是把這個類指針轉換成了靜態的。有了上面的這三段代碼,若是想在某個類A裏面聲明一個私有類,直接來一個Q_D(A),再Q_DECLARE_PRIVATE(A)就能夠了。post

2、宏和模板的展開ui

  展開前一個宏和後一個宏的一部分,成了this

1 #define Q_D(A) APrivate *const d= d_func()
1 inline APrivate* d_func() { return reinterpret_cast<APrivate *>(qGetPtrHelper((d_ptr));}

  上面這個函數qGetPtrHelper調用的輸入值是QObject類裏面的一個成員變量,d_ptr指針,定義以下:spa

1     QScopedPointer<QObjectData> d_ptr;

   根據模板函數調用返回的仍然是一個QScopedPointer<QObjectData>類型的變量,也就是一個指向QObjectData類型的智能限域指針。最後調用reinterpret_cast從新解釋前面獲得的指針,把它變成指向APrivate類型的。翻譯

  這裏的QObjectData的定義也在QObject裏面指針

 1 class Q_CORE_EXPORT QObjectData {  2 public:  3     virtual ~QObjectData() = 0;  4     QObject *q_ptr;  5     QObject *parent;  6  QObjectList children;  7 
 8     uint isWidget : 1;  9     uint blockSig : 1; 10     uint wasDeleted : 1; 11     uint isDeletingChildren : 1; 12     uint sendChildEvents : 1; 13     uint receiveChildEvents : 1; 14     uint isWindow : 1; //for QWindow
15     uint unused : 25; 16     int postedEvents; 17     QDynamicMetaObjectData *metaObject; 18     QMetaObject *dynamicMetaObject() const; 19 };

  至於QScopedPointer,有點複雜,核心思想是一個不須要本身銷燬的指針。

3、繼承和調用

  本身寫一個類MyQFileSystemModel繼承QFileSystemModel,在MyQFileSystemModel中使用Q_D宏,會出現錯誤:

 1 C:\Qt\Qt5.9.2\5.9.2\mingw53_32\include\QtWidgets/qfilesystemmodel.h: In constructor 'MyQFileSystemModel::MyQFileSystemModel()':  2 C:\Qt\Qt5.9.2\5.9.2\mingw53_32\include/QtCore/qglobal.h:1002:28: error: 'QFileSystemModelPrivate* QFileSystemModel::d_func()' is private
 3      inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr)); } \  4                             ^
 5 C:\Qt\Qt5.9.2\5.9.2\mingw53_32\include\QtWidgets/qfilesystemmodel.h:152:5: note: in expansion of macro 'Q_DECLARE_PRIVATE'
 6  Q_DECLARE_PRIVATE(QFileSystemModel)  7      ^
 8 C:\Qt\Qt5.9.2\5.9.2\mingw53_32\include/QtCore/qglobal.h:1016:54: error: within this context  9  #define Q_D(Class) Class##Private * const d = d_func()
10                                                       ^
11 ..\student\myqfilesystemmodel.cpp:5:5: note: in expansion of macro 'Q_D'
12      Q_D(const QFileSystemModel); 13      ^
14 C:\Qt\Qt5.9.2\5.9.2\mingw53_32\include/QtCore/qglobal.h:1016:43: warning: unused variable 'd' [-Wunused-variable] 15  #define Q_D(Class) Class##Private * const d = d_func()
16                                            ^
17 ..\student\myqfilesystemmodel.cpp:5:5: note: in expansion of macro 'Q_D'

   意思是,d_func()是QFileSystemModel這個類裏面的私有函數,對於c++的私有函數,子類是不可以繼承的。

4、q指針

  寫一個MyQFileSystemModel的私有類MyQFileSystemModelPrivate,就可使用Q_Q宏,從私有類引用對應的公有類了。

 1 #ifndef MYQFILESYSTEMMODEL_P_H  2 #define MYQFILESYSTEMMODEL_P_H
 3 #include <myqfilesystemmodel.h>
 4 class MyQFileSystemModelPrivate;  5 class MyQFileSystemModel;  6 QT_BEGIN_NAMESPACE  7 class MyQFileSystemModelPrivate: public QFileSystemModelPrivate  8 {  9 public: 10     //MyQFileSystemModelPrivate(MyQFileSystemModel * parent):q_ptr(parent){}
11 public: 12  Q_DECLARE_PUBLIC(MyQFileSystemModel) 13     MyQFileSystemModel *q_ptr; 14 }; 15 QT_END_NAMESPACE 16 #endif // MYQFILESYSTEMMODEL_P_H

  這裏面須要尤爲注意的是,MyQFileSystemModel *q_ptr;這一行是不能少的。c++的static_cast是對被轉換的類型有限制的。若是B類繼承了A類,那麼從B類轉換成A類是徹底沒有問題的。可是,若是想把A類轉換成B類,就要求B類裏面,問題就出現了,A類可能沒有B類那麼豐滿,轉換出來的類多是殘廢的,因此通常狀況下,這種轉換是不能成功的。除非:B類裏面包含了一個指向A類的指針。若是把q_ptr指針聲明去掉了,會報這個錯誤:

1 C:\Qt\Qt5.9.2\5.9.2\mingw53_32\include\QtCore\qglobal.h:1012: error: invalid static_cast from type 'QObject*' to type 'MyQFileSystemModel*'
2      inline Class* q_func() { return static_cast<Class *>(q_ptr); } \ 3                                                                ^

5、私有類函數調用

  Qt中的公有類和私有類關係密切,私有類的函數是不可以直接使用的。要是想用,必定要經過其餘類調用。在Qt編譯的時候,QFileSystemModel.h是一堆聲明,會導出不少函數到QtWidget.dll裏面去。值得注意的是,這裏導出的只是這個文件裏所聲明的類、函數和變量,而不會導出只在私有類頭文件QFileSystemModel_p.h裏面聲明過的函數和變量。

  固然,編譯的時候,也會引用QFileSystemModelPrivate.h和QFileSystemModel.cpp裏的聲明以及定義。通常狀況下,咱們只是Qt的使用者,在windows系統下,只要下載安裝就好,不須要本身再編譯了。可是當咱們想使用私有類作一些更深刻的定製的時候,但願可以調直接調用私有類的成員。這時候,若是隻是include了.h文件,就會報undefined reference錯誤。也就是說,編譯能過,連接過不了,找不到私有類成員。

   解決的方法就是將對應的cpp文件也包含進工程目錄裏面,但這時候會有新的問題出現。有些類的實現已經編譯到庫函數裏面了,這時候又在cpp文件從新實現,會報警告:redeclared without dllimport attribute。只須要把cpp文件中的實現函數刪除掉就能夠了。

  雖然經過上面的方法,能夠實現私有類的使用,可是值得注意的是,已經編譯好的二進制文件dll裏面的同名私有函數還在起着做用。仍是用QFileSystemModel爲例,它已經被Qt編譯好放到了QWidget.dll裏面了。在此dll文件裏也會有QFileSystemModelPrivate的函數(只是不會有直接指向外部的聲明,外部不能直接連接引用而已),不然私有類就徹底沒用了。當新的工程要使用到QFileSystemModel,而QFileSystemModelPrivate被間接地引用,使用的版本就是以前編譯好的成品。當新的工程要直接調用私有類函數的時候,纔是用的新的版本。

  用Dependency打開Qt5Widgets.dll看到私有類的函數名形式以下:

1 _ZN16QFileSystemModelC1ER23QFileSystemModelPrivateP7QObject

  這可能只是一個構造函數,在Qt5Widgets.dll裏只有兩個上面這種QFileSystemModelPrivate的函數,可見大多數私有類的函數沒有導出。而直接用本節方法編譯出來的QFileSystemModelPrivate.dll裏面的私有成員函數形式以下:  

1 _ZNK23QFileSystemModelPrivate4nodeERK11QModelIndex

  表明形參是QModelIndex的私有類函數node,像這樣的函數還有不少,它們與私有類的.h文件可以對應起來。

相關文章
相關標籤/搜索