Qt之二進制兼容


一、回顧

    使用qt2年多了,但是還是覺得很陌生,總是會被qt搞的很緊張,有時候當我自信滿滿的打開幫助文檔,搜索某個已知的類時,由於筆誤敲錯了一個字母而出現了另外一個類,不過奇怪的是還真有這么一個類,哎!!!我怎么都不知道呢!看來qt的東西還真不是一般的多,隨便一個筆誤都可能發現新的東西。特別是qt現在已經發布了qt5.7版本,相對於qt4的時代那真是天差地別,就我個人而言,我現在用的是qt5.6.1-1,因為qt5.6版本官方聲稱維護2年的時間。qt5.6取消了webkit模塊,如果需要使用可以自行編譯,我自己也對qt5.6.1-1的整個模塊進行了編譯,有需要的小伙伴可以參考msvc2013編譯qt5.6源碼

    本文主要是想講述qt的一個代碼書寫方式,當然了這樣說有些大,其實就是qt他的源碼在分層上是把成員都單獨拉到一個filename_p.h的頭文件里,這樣做的有好處也有壞處,下面是我自己的觀點:

  優點 缺點
qt

1、龐大的代碼容易管理

2、成員的私有實現和具體對外開放的接口分離

1、閱讀性差

2、可擴展性差

表1 qt優缺點

    上述缺點都是我個人認為,其中可擴展性差不是說qt不能擴展,而是擴展的時候有一定限制,因為qt的一個功能實現的最基本單元就是filename.h、filename.cpp和filename_p.h,通常情況下我們只能拿到filename.h,而私有的filename_p.h文件拿不到,所以不能進行對齊進行擴展,這也導致了一些操作不能很好的而進行,除非qt源碼給我們了相關接口,否則我們是很難去維護私有的filename_p.h的實現,比如:qt的qcharts模塊,這個模塊是qt5.7才正式開源的模塊,當然了有興趣的同學可以自己手動在自己的當前qt模塊下編譯qtcharts,然后將開發所需要的庫都加入到自己當前的qt版本中。

    為什么說提到qcharts模塊呢,是因為我之前看過大概的實現原理,他也是一貫的qt代碼作風,那就是不容易閱讀,每一個對外暴露的接口,僅僅是接口而已,而他具體的實現都是隱藏在另外一個沒有暴露給用戶的接口里,如下圖1所示,QBarSeries是對外開放的接口,但是其具體實現是在BarSeries內完成的,這樣就導致了我們只能對QBarSeries進行重寫,而不能修改BarSeries類,或者對齊進行重寫。

圖1 QBarSeries幫助文檔

    呵呵呵。。。說的有些多了,可能喜歡qt的人會來罵我,以上完全是我個人的理解,有不對的地方請指正。其實我個人還是挺喜歡qt的,最起碼在封裝上我覺得很好,接口的命名上也很容易理解,比較容易上手,對於一個做C++界面工作的開發來說,算是一個不錯的選擇。

   下面進入本片文章的主題,本片文章我主要是想分析下qt的私有實現結構,其實也就是我們所謂的impl實現原則。

二、思路

    說起qt的代碼管理,我覺着真的非常好,畢竟是第一個龐大的庫,如果沒有一個優秀的代碼結構,對於C++這樣編譯性的語言,那真的是一個噩夢,多么優秀的程序員可能一天就處於編譯代碼的等待中。。。

    qt在類的管理上,講成員函數單獨封裝到了一個對於的_p.h的頭文件中,然后可能還是提供相應的私有接口,為什么說私有呢,因為我們拿不到,但是qt的類中卻可以拿到,這個在下一節中我會做一些簡單的分析。

    qt代碼分工:filename.h、filename.cpp和filename_p.h,而這3個文件是用一組宏類關聯起來,並使用,有一定的封裝性。后續文章中,filename.h中的類我就用Class描述,filename_p.h中的類我就用ClassPrivate描述。

三、關鍵宏

  • Q_DECLARE_PUBLIC
  • Q_DECLARE_PRIVATE
  • Q_D
  • Q_Q

1、Q_DECLARE_PUBLIC

    這個宏是用來在filename_p.h類中使用得,為的就是可以拿到一個Class的類的一個指針,並且它是Class的友元類,Class類是一個泛指,指的是filename.h縱的類。

1 #define Q_DECLARE_PUBLIC(Class)                                    \
2     inline Class* q_func() { return static_cast<Class *>(q_ptr); } \
3     inline const Class* q_func() const { return static_cast<const Class *>(q_ptr); } \
4     friend class Class;

2、Q_DECLARE_PRIVATE

    這個宏是在filename.h類中使用,和Q_DECLARE_PUBLIC宏是對於的,它是ClassPrivate類的友元,並且可以拿到一個ClassPrivate的指針

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;

3、Q_D和Q_Q

    這兩個宏就比較好理解了,僅僅是快捷的拿到一個指針,關於這個指針是怎么聲明?在哪兒聲明?后續都會提到

 1 #define Q_D(Class) Class##Private * const d = d_func()2 #define Q_Q(Class) Class * const q = q_func() 

四、結構說明

    首先我先來回答一下,第三節中第三小節所提到的問題,關於Class和ClassPrivate這兩個指針存儲的地方,就是他們分別的功能基類,而且必須是保護或者是公有成員。在后續的子類中都不需要再聲明該類指針

    作為父類都必須提供至少兩個構造函數,一個是用來聲明對象自己的,一個是用來初始化子類的,這樣說可能會有點兒不好理解,那么我就把qwidget的源碼搬上來做以說明

圖1 QWidget公有構造函數

圖2 QWidget保護構造函數

    圖1和圖2分別是QWidget的兩個構造函數,只是訪問權限不一樣,保護函數只能在子類中訪問,那么同學們是不是有些明白了,基類的ClassPrivate就是通過這個保護的構造方法類初始化,子類傳遞給父類的ClassPrivate指針,能賦值給父類的成員指針,那么這個指針肯定有父子關系的,那么又一個關鍵點浮出水面,那就是ClassPrivate也是可以繼承的,這樣很好的達到了私有數據繼承的問題,如圖3就是調用了子類的私有結構來初始化父類私有結構

圖3 子類構造函數

    零散的說了這么多,qt二進制兼容性也說了個大概,只是沒有系統的demo,那么下面我貼一個完整的測試頭文件夾,關於demo,后續我會給一個完整的

 1 struct BasicPlotPrivate;
 2 //基類 保存私有結構指針 並提供一個保護的構造函數
 3 class BasicPlot : public QWidget
 4     , public PlotCallback
 5 {
 6     Q_OBJECT
 7     Q_DECLARE_PRIVATE(BasicPlot)
 8 
 9 protected:
10     BasicPlot(QWidget * parent = nullptr);
11 public:
12     ~BasicPlot();
13 
14 protected:
15     BasicPlot(BasicPlotPrivate & pd, QWidget * parent = nullptr);
16 
17 protected:
18     QScopedPointer<BasicPlotPrivate> d_ptr;
19 };
20 //基類的私有結構 保存一個基類指針
21 struct BasicPlotPrivate
22 {
23     Q_DECLARE_PUBLIC(BasicPlot)
24 
25     BasicPlot * q_ptr;
26 };
27 //子類 不需要聲明指針 公有構造函數實現類似於圖3
28 class FinancialPlot : public BasicPlot
29 {
30     Q_OBJECT
31     Q_DECLARE_PRIVATE(FinancialPlot)
32 
33 protected:
34     FinancialPlot(QWidget * parent = nullptr);
35 
36 public:
37     ~FinancialPlot();
38 
39 protected:
40     FinancialPlot(FinancialPlotPrivate & pd, QWidget * parent = nullptr);
41 };
42 //子類私有結構 繼承與父類私有結構
43 struct FinancialPlotPrivate : public BasicPlotPrivate
44 {
45     FinancialPlotPrivate() : BasicPlotPrivate(){}
46     Q_DECLARE_PUBLIC(FinancialPlot)
47 };

五、示例下載

    關於示例下載,我將會在下一節qcustomplot使用分享中提供一份demo,而這個dmeo的實現就是使用的qt這種代碼組織方式。

    補充:

  基於QCustomPlot2.0.0的二次開發

 

如果您覺得文章不錯,不妨給個 打賞,寫作不易,感謝各位的支持。您的支持是我最大的動力,謝謝!!! 

 

  


很重要--轉載聲明

  1. 本站文章無特別說明,皆為原創,版權所有,轉載時請用鏈接的方式,給出原文出處。同時寫上原作者:朝十晚八 or Twowords
  2. 如要轉載,請原文轉載,如在轉載時修改本文,請事先告知,謝絕在轉載時通過修改本文達到有利於轉載者的目的。 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM