12-4_Qt 5.9 C++开发指南_创建和使用共享库

文章目录

  • 1. 创建共享库
  • 2. 使用共享库
    • 2.1 共享库的调用方式
    • 2.2 隐式链接调用共享库
    • 2.3 显式链接调用共享库

1. 创建共享库

除了静态库,Qt 还可以创建共享库,也就是 Windows 平台上的动态链接库。动态链接库项目编译后生成 DLL 文件,DLL 文件在 windows 平台上应用广泛。DLL 文件是在应用程序运行时加载的,不像静态库那样在编译期间就连编到应用程序里。若更新了 DLL 文件版本,只要接口未变,应用程序依然可以调用。

创建共享库项目,单击Qt Creator 的“File”->“New File or Project”菜单项,在 New File orProject 对话框中选择 Projects 组里的 Library,在右侧的具体类别中再选择 C++ Library,单击“Choose*·.”按钮后出现如下图 所示的向导对话框。

在这里插入图片描述

在此对话框的 Type 下拉列表框里选择 Shared Library,并给项目命名,例如 mySharedLib,再选择项目保存目录。单击“Next”按钮后选择编译器,下一步选择需要包含的 Qt 模块,再下一步是类定义页面,在其中输入类的名称,这里仍然输入类名称为QWDialogPen,再下一步结束即可。

由向导生成的 mySharedLib项目包含文件 mySharedLib.pro、qwdialogpen.h 和qwdialogpen.cpp。此外还有一个特殊的头文件 mysharedlib global.h。结构如下图所示:

在这里插入图片描述
mysharedlib global.h文件的内容如下:

#ifndef MYSHAREDLIB_GLOBAL_H
#define MYSHAREDLIB_GLOBAL_H#include <QtCore/qglobal.h>#if defined(MYSHAREDLIB_LIBRARY)
#  define MYSHAREDLIBSHARED_EXPORT Q_DECL_EXPORT
#else
#  define MYSHAREDLIBSHARED_EXPORT Q_DECL_IMPORT
#endif#endif // MYSHAREDLIB_GLOBAL_H

这里定义了符号MYSHAREDLIBSHARED_EXPORT用于替代Qt的宏Q_DECL_EXPORT或Q_DECL_IMPORT。

一个共享库导出给用户使用的类、符号、函数等都需要用宏Q_DECL_EXPORT来定义导出,一个使用共享库的应用程序需要通过Q_DECL_IMPORT导入共享库里的可用对象。

在mySharedLib.pro文件中增加了符号MYSHAREDLIB_LIBRARY的定义,下面是mySharedLib.pro文件的主要内容:

QT       += widgetsTARGET = mySharedLib
TEMPLATE = libDEFINES += MYSHAREDLIB_LIBRARY
DEFINES += QT_DEPRECATED_WARNINGS

自动生成的 qwdialogpen.h 文件里的内容是对 QWDialogPen 类的定义,在类名称前使用了宏MYSHAREDLIBSHARED_EXPORT,定义QWDialogPen 为一个导出的类。

#ifndef QWDIALOGPEN_H
#define QWDIALOGPEN_H#include    <QDialog>
#include    <QPen>
#include    "mysharedlib_global.h"namespace Ui {
class QWDialogPen;
}class MYSHAREDLIBSHARED_EXPORT QWDialogPen : public QDialog
{ //QPen属性设置对话框Q_OBJECT
private:QPen    m_pen; //成员变量
public:explicit QWDialogPen(QWidget *parent = 0);~QWDialogPen();void    setPen(QPen pen); //设置QPen,用于对话框的界面显示QPen    getPen(); //获取对话框设置的QPen的属性static  QPen    getPen(QPen  iniPen, bool &ok);  //静态函数private slots:void on_btnColor_clicked();
private:Ui::QWDialogPen *ui;
};#endif // QWDIALOGPEN_H

将 12.3 节静态库项目里的文件 qwdialogpen.h、qwdialogpen.cpp 和 qwdialogpen.ui 复制到本项目目录下,覆盖自动生成的初始文件,但是修改文件 qwdialogpen.h 里的类的定义,在类名称前增加MYSHAREDLIBSHARED_EXPORT 宏,并加入mysharedlib global.h 的包含语句。

项目的文件准备好之后就可以编译生成 DLL 文件,根据使用的编译器不同,生成的文件有些区别。

  • 若使用 MSVC 编译,编译后会生成 mySharedLib.dll 和 mySharedLib.lib 两个文件,mySharedLib.dll 在运行应用程序时调用,mySharedLib.lib 在应用程序隐式调用动态链接库时使用
  • 若使用 MinGW 编译,编译后会生成 mySharedLib.dll 和 libmySharedLib.a 两个文件,mySharedLib.dll 在运行应用程序时调用,libmySharedLib.a 在应用程序隐式调用动态链接库时使用。

采用 debug 和release 不同模式生成的文件只能当应用程序在 debug 或release 模式下编译或调用。

由于动态库的代码和上篇静态库的基本一样,只是多了mysharedlib global.h文件,这里就不再赘述了。

2. 使用共享库

2.1 共享库的调用方式

调用动态链接库有两种形式,隐式链接 (implicit linking)调用和显式链接 (explicit linking)调用。

  • 隐式链接调用是在编译应用程序时,有动态库的 lib 文件(或a 文件)和 h 头文件,知道 DLL中有哪些接口类和函数,编译时就隐式地生成必要的链接信息,使用 DLL 中的类或函数时根据h头文件中的定义使用即可。应用程序运行时将自动加载 DLL 文件。隐式链接调用主要用于同一种编程软件(如 Qt)生成的代码的共享。

  • 显式链接调用是只有 DLL 文件,知道 DLL 里的函数原型,使用 QLibrary 类对象在应用程序里动态加载 DLL 文件,声明函数原型,并使用 DLL 里的函数。这种方式需要在应用程序里声明函数原型,并解析 DLL 里的函数。

2.2 隐式链接调用共享库

创建一个基于QMainWindow 的应用程序 shareLibUser,程序功能与 12.3 节的 LibUser 项目一样,将LibUser 项目的 mainwindow 相关3 个文件mainwindow.h、mainwindow.cpp 和mainwindow.ui复制到 shareLibUser 项目下,替换自动生成的文件。

在 shareLibUser 项目文件目录下新建一个 include 目录,将 mySharedLib 项目的两个头文件qwdialogpen.h 和 mysharedlib_global.h 复制到此目录下。若使用 MSVC 编译器,则将 release 版本的mySharedLib.lib 复制到此目录下,debug 版本的 mySharedLib.lib 更名为 mySharedLibd.lib 复制到此目录下;若使用 MinGW 编译器,则复制 release 版本的 libmySharedLib.a,debug 版本的libmySharedLib.a 更名为 libmySharedLibd.a 复制到此目录下。

为应用程序增加动态链接库,右键单击 shareLibUser 项目节点,在快捷菜单里单击“Add Library···”菜单项,在出现的向导对话框里首先选择添加的库类型为“Extermal Library”,在向导第二步设置导入的动态库文件(见下图)。

在这里插入图片描述
在上图中,选择项目 include 目录下的 mySharedLib.lib 文件或libmySharedLib.a 作为库文其他设置如上图所示。

完成后在shareLibUser.pro 文件中自动增加项目设置的语句如下:(目的也是为了下面的程序,也可以不通过UI操作,自己写相应的代码)

win32:CONFIG(release, debug|release): LIBS += -L$$PWD/include/ -lmySharedLib
else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/include/ -lmySharedLibdINCLUDEPATH += $$PWD/include
DEPENDPATH += $$PWD/include

项目编译时,会根据当前是 release 还是 debug 模式,自动添加相应的库文件。这里添加库文件只是使用了动态库的导出定义,而不是将库的实现代码连接到应用程序的可执行文件里。主窗体类 MainWindow 的功能与上篇静态库 的程序完全一致,调用共享库里的类OWDialogPen也无需特别说明,只需包含头文件 qwdialogpen.h 即可。

注意:必须将动态链接库文件 mySharedLib.dll复制到可执行文件的目录下,程序才可以正常运行mySharedLib.dll 的 debug 和 release 版本必须分别用于应用程序的 debug 和 relcase 版本,否则运行时出错。

使用动态链接库可以很方便地扩展应用程序的功能,但是 DLL 文件需要随应用程序一起发布,并且编译 DLL和应用程序的 Qt 版本最好保持一致,否则需要考虑二进制兼容问题。关于二进制兼容可以参考:Qt源代码中二进制兼容及d、q指针的理解

2.3 显式链接调用共享库

显式链接调用共享库是在应用程序运行时才加载共享库文件,并调用库里的函数的。应用程序编译时无需共享库的任何文件,只需知道函数名和函数的原型即可。所以,这种方式可以调用其他语言编写的 DLL 文件,例如用 Delphi 生成的一个DLL 文件。

显式链接调用共享库是通过 QLibrary 类实现的。QLibrary 是与平台无关的,用于在运行时载入共享库,一个 QLibrary 对象只对一个共享库进行操作。

一般在 QLibrary 的构造函数中传递一个文件名,可以是带路径的绝对文件名,也可以是不带后缀的单独文件名。QLibrary 会根据运行的平台自动查找不同后缀的共享库文件,例如 Unix 上是.so”,Mac上是“.dylib”,Windows 上是“.dll”。

作为示例,用 Delphi编写一个 DLL 项目,生成一个 DelphiDLL.dll文件,这个文件里只有一个函数,函数的原型为:

function triple(N;integer):integer;

它会计算传递参数N的3倍值并返回。

在Qt Creator 里创建一个基于 QMainWindow 的应用程序DelphiDLLUser,设计一个简单的界面,运行时下图所示。单击按钮时将根据输入,调用动态链接库 DelphiDLL.d11里的triple()函数,计算结果并显示在输出编辑框里。

按钮的槽函数代码如下:

void MainWindow::on_pushButton_clicked()
{QLibrary myLib("DelphiDLL");if (myLib.isLoaded())QMessageBox::information(this,"信息","DelphiDLL.DLL已经被载入,第1处");typedef int (*FunDef)(int); //函数原定定义FunDef myTriple = (FunDef) myLib.resolve("triple"); //解析DLL中的函数int V=myTriple(ui->spinInput->value()); //调用函数ui->spinOutput->setValue(V);if (myLib.isLoaded())QMessageBox::information(this,"信息","DelphiDLL.DLL已经被载入,第2处");
}

在定义QLibrary对象实例 myLib 时传递了共享库文件名“DelphiDLL”,这里不需要给出后缀名。DelphiDLL.dll 文件必须在应用程序同一目录、系统目录或可搜索目录下。

QLibrary 有几个函数用于 DLL文件的载入与卸载:

  • load()用于手动载入 DLL 文件到内存里,一般无需手工调用此函数,在DLL里的函数第一次被使用时 QLibrary 会自动调用此函数;

  • isLoaded()用于判断 DLL是否已经被载入内存;

  • unload()用于将DLL从内存中卸载。

一个动态链接库在内存里只能有一个实例,也就是即使有多处调用了这个动态链接库里的函数,它也只会被载入一次,如果不是所有的实例都使用 unload()卸载它,那么它会在应用程序退出时才卸载。

在槽函数on_pushButton_clicked()的代码里,有两处QMessageBox 显示信息。在运行应用程序,第一次单击按钮时,只有第 2 处信息框显示,说明声明了 QLibrary 对象后,动态链接库没有立即被载入内存;第二次单击按钮时,两处信息框会先后显示,说明动态链接库上次载入内存后还在内存里。

显式调用动态链接库里的函数,需要声明函数原型的类型,即:

typedef int (*FunDef)(int); //函数原定定义

然后使用QLibrary的resolve()函数解析需要调用的函数。

FunDef myTriple = (FunDef) myLib.resolve("triple"); //解析DLL中的函数

这样就定义了一个函数 myTriple,用于实现 DLL文件里的函数"triple"的功能,当然重新声明的函数名称可以和 DLL 里的函数名称完全相同。

如果 DelphiDLL.dll 文件没有复制到应用程序目录下,则编译和启动应用程序都不会出错,只有单击按钮调用 DLL 里的函数时才会出错。所以,要使应用程序正常运行,需要将 DelphiDLL.dIl文件复制到应用程序目录下。(实际操作时发现即使复制进去,点击后会显示“程序异常结束”,后期实践时再测试)
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/19355.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

docker 保存和载入镜像

查看本机docker镜像 docker images保存镜像 docker save -o /home/space/work1/docker_qnx7.1.tar.gz a01ee6d74c36复制镜像到其他服务器 scp /home/space/work1/docker_qnx7.1.tar.gz XXXIP:/home/dell/work1/登录新 服务器操作 docker load -i docker_qnx7.1.tar.gz载入后…

网络安全/信息安全—学习笔记

一、网络安全是什么 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领域&#xff0c;都有攻与防两面…

MySQL 的解析器以及 MySQL8.0 做出的改进 | StoneDB技术分享 #2

设计&#xff1a;小艾 审核&#xff1a;丁奇 编辑&#xff1a;宇亭 作者&#xff1a;柳湛宇&#xff08;花名&#xff1a;乌淄&#xff09; 浙江大学-软件工程-在读硕士、StoneDB 内核研发实习生 一、MySQL 的解析器 MySQL 所使用的解析器&#xff08;即 Lexer 和 Parser …

【Git】git reset 版本回退 git rm

前言 在日常开发时&#xff0c;我们经常会需要撤销之前的一些修改内容或者回退到之前的某一个版本&#xff0c;这时候reset命令就派上用场了 git reset 用法1——所有文件回退到某个版本 1、使用git reflog查看要回退的commit对象 2、使用git reset [-- hard/soft /mixed] …

算法通关村第二关——反转链表白银笔记

文章目录 1.链表指定区间翻转2.两两交换链表中的节点 1.链表指定区间翻转 LeetCode 92.反转链表 解法一&#xff1a;头插法。利用虚拟节点进行反转&#xff0c;因为头节点有可能发生变化&#xff0c;比如 left1 那么需要 dummyNode.next 记录头结点&#xff0c;使用虚拟头节点…

Arcgis通过模型构建器计算几何坐标

模型 模型中&#xff0c;先添加字段&#xff0c;再计算字段 计算字段 模型的计算字段中&#xff0c;表达式是类似这样写的&#xff0c;其中Xmin表示X坐标&#xff0c;Ymin表示Y坐标 !Shape.extent.Xmin!类似计算面积 !shape.area!

突破游戏行业天花板,“技术外溢”成趋势

文 | 螳螂观察 作者 | 余一 受游戏版号发放的“放缓”、人口结构的调整&#xff0c;过去两年国内游戏行业过得并不算好。前不久据相关机构发布的数据显示&#xff0c;2022年中国游戏市场实际销售收入2658.84亿元&#xff0c;同比减少306.29亿元&#xff0c;下降10.33%。且游戏…

创建个人博客(在文章的列表页,根据文章标题和文章内容实现搜索)

1. 在视图文件增加搜索表单&#xff1a; 在文章列表页的视图文件中&#xff0c;增加一个搜索表单&#xff0c;包含一个文本搜索框和一个提交按钮 <% form_tag articles_path, method: :get do %><% text_field_tag :title, params[:title], placeholder: "搜索…

海康视频插件VideoWebPlugin在vue中的实现

一,将js文件放在public文件下 二,在index中全局引入 三.在视频页面写方法,创建实例,初始化,我写的是1*4屏的 <template><!--视频窗口展示--><div idplayWnd classNameplayWnd refplayWnd styleleft: 0; bottom: 0;height: 902px;width: 60vw></div>&…

Eureka 学习笔记2:EurekaClient

版本 awsVersion ‘1.11.277’ EurekaClient 接口实现了 LookupService 接口&#xff0c;拥有唯一的实现类 DiscoveryClient 类。 LookupService 接口提供以下功能&#xff1a; 获取注册表根据应用名称获取应用根据实例 id 获取实例信息 public interface LookupService<…

HTTP杂谈之Referer和Origin请求头再探

一 关于Referer和Origin的汇总 1) 知识是凌乱的,各位看官看个热闹即可2) 内容不断更新1、理解有盲区,需要及时纠正2、内容交叉有重复,需要适当删减3、扩展视野3) 以下内容都与Referer和Origin请求头有关联 nginx防盗链 HTTP杂谈之Referrer-Policy响应头 iframe标签referre…

物联网|可变参数的使用技巧|不一样的点灯实验|访问外设的寄存器|操作寄存器实现点灯|硬件编程的基本流程-学习笔记(11)

文章目录 可变参数的使用技巧第三阶段-初级实验Lesson5:不一样的点灯实验---学习I/O的输出 ☆点灯的电路图分析1 一起看看点灯的电路图Tip1:另一种点灯的电路Tip1:如何访问外设的寄存器2 STM32F407中操作GPIO的方法 通过直接操作寄存器实现点灯实验Tip1:硬件编程的基本流程 2代…

HTML5+CSS3小实例:带标题的3D多米诺人物卡片

实例:带标题的3D多米诺人物卡片 技术栈:HTML+CSS 效果: 源码: 【html】 <!DOCTYPE html> <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="viewport" content…

【信号去噪】基于马氏距离和EDF统计(IEE-TSP)的基于小波的多元信号去噪方法研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Linux 终端生成二维码

1、安装qrencode [rootnode1 script]# yum -y install qrencode2、输出正常的 [rootnode1 aihuidi]# echo https://blog.csdn.net/weixin_43822878?t1|qrencode -o - -t utf83、输出彩色的 [rootnode1 aihuidi]# qrencode -t utf8 -s 1 https://blog.csdn.net/weixin_4382…

魔法上网端口号被占用通过端口号找到进程并且杀掉进程随笔

Windows11系统由于魔法上网被异常关闭导致再次启动的时候报出端口号被占用问题记录以前忘记的通过端口杀掉进程相关操作。 在命令行&#xff08;winr,输入cmd&#xff09;中输入: netstat -ano 可以看到本机正在使用的ip地址和端口号如图&#xff1a; 在命令行&#xff08;wi…

算法题--找规律(构建乘积数组、剪绳子、圆圈中最后剩下的数字)

目录 找规律 构建乘积数组 原题链接 解析 核心思想 答案 剪绳子 原题链接 解析 核心思想 答案 圆圈中最后剩下的数字 原题链接 解析 核心思想 答案 找规律 需要通过列举多个示例&#xff0c;从多个示例的输入到输出中得到规律去普遍化。 构建乘积数组 给定…

【娱乐圈明星知识图谱2】信息抽取

目录 1. 项目介绍 2. 信息抽取介绍 3. ChatGPT 信息抽取代码实战 4. 信息抽取主逻辑 5. 项目源码 1. 项目介绍 利用爬虫项目中爬取的大量信息 【娱乐圈明星知识图谱1】百科爬虫_Encarta1993的博客-CSDN博客娱乐圈明星知识图谱百度百科爬虫百度百科爬虫百度百科爬虫百度百…

redisson分布式锁学习

什么是分布式锁? 当有多个线程并发访问同一共享数据时,如果多个线程同时都去修改这个共享数据,且修改操作不是原子操作,就很有可能出现线程安全问题&#xff0c;而产生线程安全问题的根本原因是缺乏对共享数据访问的同步和互斥。 为了解决这个问题&#xff0c;通常我们的做法…

【Golang 接口自动化07】struct转map的三种方式

目录 背景 struct转map 使用json模块 使用reflect模块 使用第三方库 测试 总结 资料获取方法 背景 我们在前面介绍过怎么使用net/http发送json或者map数据&#xff0c;那么它能不能直接发送结构体数据呢&#xff1f;我们今天一起来学习结构体struct转map的三种方法&am…