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. 如要转载,请原文转载,如在转载时修改本文,请事先告知,谢绝在转载时通过修改本文达到有利于转载者的目的。 

posted @ 2016-11-06 19:14  朝十晚八  阅读(2211)  评论(0编辑  收藏  举报

返回顶部