| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> C++知识库 -> 【QT】二进制兼容和Qt的相关设计 -> 正文阅读 |
|
[C++知识库]【QT】二进制兼容和Qt的相关设计 |
二进制兼容? ? ? ? SDK的开发中,最需要注意的就是接口的设计,因为SDK一经投入使用,其中的任何一个函数都可能被某个应用程序调用,如果接口设计不合理,导致频繁地更改,则会影响上游的应用程序,要修改上游的应用程序的代码来适应新的接口。 ????????其次就是要保证二进制兼容。本篇文章主要学习QT中对实现二进制兼容做的一些设计。 ????????什么是二进制兼容呢? ????????假设有一个库Library,当前版本为v1.0,有一个应用程序App,App项目以动态链接的方式调用了v1.0版本的Library(App项目中应该包含Library中某些用到的头文件和动态链接库)。此时库Library进行了版本升级,从v1.0升级到V1.1,且已经部署到App所在的系统环境中。此时App项目中仍用到的是v1.0的Library的头文件,而系统中的Library的动态库已经升级成V1.1。在这种情况下,如果App无需重新编译,而能继续顺利运行,则称库Library是二进制兼容的。 ? ? ? ? 什么样的操作会破坏二进制兼容呢? ? ? ? ? 引用Qt wiki上的例子:
? ? ? ? 假设V1.0库中的一个头文件中定义了两个类Widget和Label,其中Label继承自Widget,Widget中包含一个成员变量m_geometry,Label中包含一个成员变量m_text以及一个成员函数text()。之后,问题来了!假设之后的维护中,需要对Widget增加一个样式的调整,此时增加了成员变量m_stylesheet。
? ? ? ? 此时,如果系统中将lib从v1.0升级成v1.1,并且不重新编译应用程序App的话,直接运行App会发现:程序崩溃! ? ? ? ? 那为什么程序会崩溃呢?这就要引入C++对象模型的一些基础知识。 前提知识:? ? ? ?
? ? ? ? 好了,有了前提知识后,来看一下我们的例子中的内存模型示意图。假设在v1.0时,一个Label的实例化对象可能是下表左边的样子,m_geometry变量在offset 0的位置,m_text在offset 1的位置。而在v1.1中,由于添加了变量m_stylesheet,导致m_stylesheet出现在了offset 1的位置,而m_text移到了offset2的位置。这样的话,如果不重新编译App项目,则程序运行时,仍然去offset 1的位置去寻找m_text,然而offset 1的位置存放的已经不再是m_text,而是m_stylesheet,所以程序会崩溃。
? ? ? ? 搬运整理了一下大佬整理的常见的会破坏二进制兼容的操作:
? ? ? ? 不会破坏二进制兼容的常见操作有:
总结来讲就是: 任何改变外部类的对象模型大小或布局的操作都会破坏二进制兼容! ????????SDK为什么要保证二进制兼容呢? ? ? ? ? 上面啰嗦了一大堆,大家应该也能知道了。如果SDK不是二进制兼容的,那么每次升级SDK时,都需要重新编译用到该SDK的应用程序。当SDK被其他应用程序广泛应用时,会带来非常大的影响。 私有数据保护? ? ? ? 像QT这种成熟的大型框架,其实现二进制兼容的方法很值得重点学习一下,其中主要的手段就是进行私有数据保护。 ? ? ? ? 在C++程序开发中,一个项目通常由多个类组成,每个类由一个.h头文件和一个.cpp文件组成。头文件中通常包含成员函数的声明和成员变量的定义,而在.cpp文件编写函数的实现以及成员变量的初始化和逻辑上的操作。 ? ? ? ? 这样做,就标准C++及面向对象编程来说,没有什么问题。但是如果我们开发的是一个库,需要将头文件交给使用者,虽然用了private修饰,用户不能操作私有成员,但是仍然能够看到私有成员。此外,更重要的是,如果将来需要对已发布的V1版本的库进行性能优化,对接口函数没有调整,仅对内部的实现方式进行优化,此时可能需要新增成员变量或者移除旧的成员变量,这个时候就必须更改头文件中的内容,而头文件中发生了变化,就需要重新编译所有的应用项目。? ? ? ? ? 而在Qt中,设计者把与内部实现紧密联系的私有的数据以及相关函数放在一个专门的数据类里面,只在公有类中向外暴露稳定的,不易变的接口,在公共类里面声明一个数据类指针指向数据类。当需要对内部实现进行优化时,仅仅需要在私有数据类中进行修改,而公共类里面的数据类指针不会有任何影响,也就保证了提供给用户使用的头文件没有任何影响,不仅减少了头文件的依赖性,而且更大程度上是保证了程序的二进制兼容。 ? ? ? ? 形如:
? ? ? ? ? 在外部类的头文件中只暴露接口函数geometry( ),而将和实现相关的函数以及成员变量封装在private类中,在外部类中定义一个指针(也就是d指针)指向private类。 Q_Q和Q_D? ? ? ? 翻看Qt源码时,会看到各种各样的宏定义,其中最常看到的就有Q_Q和Q_D两个宏,这两个宏就是用来进行私有数据封装的。源码中的宏定义:
? ? ? ? 实际上就是定义了一个d指针和q指针。那么问题又来了,刚才提到了d指针,那q指针又是干什么的呢? ? ? ? ? q指针就是在创建私有类时,传入的指向对应的公有类的指针。当在私有类的函数中需要调用公有类的方法时,就可以使用q指针进行调用。上例子:????????
? ? ? ? 至此,原理上应该大概也许啰嗦清楚了吧,实际应用中,还需要用到两个辅助的宏定义:
? ? ? ? 最终我的实操代码(简化样子)大概是这样的:
? ? ? ? ? 最后,由于对qt的元对象编译不熟悉而遇到的两个坑: ? 1.在.cpp文件中定义类,qt是不识别的,需在.cpp中最后添加#include "moc_kwidget.cpp" ? 2.如果.cpp中的私有类中需要添加Q_OBJECT宏,还需要在.cpp中最后添加#include "kwidget.moc" 个中原因,以及Qt元对象编译、Q_OBJECT之后再仔细研究。????????? 参考文章 |
|
C++知识库 最新文章 |
【C++】友元、嵌套类、异常、RTTI、类型转换 |
通讯录的思路与实现(C语言) |
C++PrimerPlus 第七章 函数-C++的编程模块( |
Problem C: 算法9-9~9-12:平衡二叉树的基本 |
MSVC C++ UTF-8编程 |
C++进阶 多态原理 |
简单string类c++实现 |
我的年度总结 |
【C语言】以深厚地基筑伟岸高楼-基础篇(六 |
c语言常见错误合集 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 | -2024/12/29 3:58:22- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |
数据统计 |