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、成员的私有实现和具体对外开放的接口分离

3、


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这种代码组织方式。

时间: 2025-01-06 09:25:21

qt之二进制兼容的相关文章

d指针在Qt上的应用及实现(d指针能实现二进制兼容)

Qt为了使其动态库最大程度上实现二进制兼容,引入了d指针的概念.那么为什么d指针能实现二进制兼容呢?为了回答这个问题,首先弄清楚什么是二进制兼容?所谓二进制兼容动态库,指的是一个在老版本库下运行的程序,在不经过编译的情况下,仍然能够在新的版本库下运行:需要经过编译才能在新版本下运行,而不需要修改该程序源代码,我们就说该动态库是源代码兼容的.要使一个dll能达到二进制兼容,对于一个结构,对于一个对象,其数据模型应该不变,若有变动,比如在类中增加数据成员或删除数据成员,其结果肯定影响对象的数据模型,

Qt 中的二进制兼容策略(简而言之就是地址不能变,剩下的就是让地址不变的技巧)

本文翻译自 Policies/Binary Compatibility Issues With C++ 二进制兼容的定义 如果程序从一个以前版本的库动态链接到新版本的库之后,能够继续正常运行,而不需要重新编译,那么我们就说这个库是二进制兼容的. 如果一个程序需要重新编译来运行一个新版本的库,但是不需要对程序的源代码进一步的修改,这个库就是源代码兼容的. 二进制兼容性可以节省很多麻烦.这使得为特定平台分发软件变得更加容易.如果不确保版本之间的二进制兼容性,人们将被迫提供静态链接的二进制文件.静态二

编写二进制兼容的库

开发小组公共库的过程中,遇到二进制兼容问题.下面是二进制兼容和代码兼容的具体定义: A library is binary compatible, if a program linked dynamically to a former version of the library continues running with newer versions of the library without the need to recompile. If a program needs to be

Qt之美(一):d指针/p指针详解(二进制兼容,不能改变它们的对象布局)

Translated  by  mznewfacer   2011.11.16 首先,看了Xizhi Zhu 的这篇Qt之美(一):D指针/私有实现,对于很多批评不美的同路人,暂且不去评论,只是想支持一下Xizhi Zhu,在引用一下Jerry Sun的话,“C++需要宏定义就像需要设计模式一样.也许你不知道,宏是图灵完全(turing complete)的,至少LISP下是这样,C/C++需要宏,几乎所有重要的C/C++库都需要和依赖宏.这些都超过咱们的想象,宏能带给我们所谓语法糖(Synta

Qt之美(一):d指针/p指针详解(解释二进制兼容,以及没有D指针就会崩溃的例子。有了D指针,所使用的对象大小永远不会改变,它就是该指针的大小。这个指针就被称作D指针)good

Translated  by  mznewfacer   2011.11.16 首先,看了Xizhi Zhu 的这篇Qt之美(一):D指针/私有实现,对于很多批评不美的同路人,暂且不去评论,只是想支持一下Xizhi Zhu,在引用一下Jerry Sun的话,“C++需要宏定义就像需要设计模式一样.也许你不知道,宏是图灵完全(turing complete)的,至少LISP下是这样,C/C++需要宏,几乎所有重要的C/C++库都需要和依赖宏.这些都超过咱们的想象,宏能带给我们所谓语法糖(Synta

【QT】二进制读取图像文件并显示

打开对话框选择文件 二进制方式读取文件 转换成图像显示 void MainWindow::showImage() { //打开文件对话框 QString lastPath="D:/Englishpath/QTprojects/DATA/videoData"; fileName = QFileDialog::getOpenFileName(this,"OpenFile", lastPath); if(fileName.isEmpty()) { QMessageBox:

嵌入式linux QT开发(一)——QT简介

嵌入式linux QT开发(一)--QT简介 一.QT简介 1.QT简介 QT是一个跨平台的C++图形用户界面库,由挪威TrollTech公司出品,目前包括Qt Creator, QtEmbedded,Qt Designer快速开发工具,Qt Linguist国际化工具等部分,Qt支持所有Linux/Unix系统,还支持Windows平台. 2.QT优点 Qt是一个跨平台的C++图形用户界面应用程序框架,提供给应用程序开发者建立艺术级的图形用户界面所需的所用功能.Qt很容易扩展,并且允许真正地组

QT 全貌一栏

QT QT使创建独立于平台的数据库应用成为可能. QT的内建驱动支持Oracle,Microsoft SQL Server,Sybase Adaptive Server,IBM DB2,PostgreSQL,MySQL,BorlandInterbase,SQLite,以及各种ODBC的数据库.QT的数据库功能完全集成到了QT设计器中,能提供数据库的生动的预览.QT包括专门的数据库组件,并且任何内建的或自定义的组件都可以数据相关: QT普遍使用Unicode并且有良好的国际化支持.QT包括QT L

Qt之美(一):d指针/p指针详解

Translated  by  mznewfacer   2011.11.16 首先,看了Xizhi Zhu 的这篇Qt之美(一):D指针/私有实现,对于很多批评不美的同路人,暂且不去评论,只是想支持一下Xizhi Zhu,在引用一下Jerry Sun的话,“C++需要宏定义就像需要设计模式一样.也许你不知道,宏是图灵完全(turing complete)的,至少LISP下是这样,C/C++需要宏,几乎所有重要的C/C++库都需要和依赖宏.这些都超过咱们的想象,宏能带给我们所谓语法糖(Synta