首页 > 其他分享 >Qt-动态库

Qt-动态库

时间:2024-07-14 16:55:18浏览次数:17  
标签:set CMAKE Qt lib DIRECTORY OUTPUT 动态

前言

1. 本文中所有的代码案例使用Qt创建,CMake构建、Windows端使用MinGW编译、Linux端使用GNU编译。

2. 完整的代码示例在绑定的资源中,审核通过后,大家可以免费下载。

一 库简介

1. 库是将一些写好的函数和变量代码整合在一起,编译生成库文件,以提供给程序使用。作用是为了复用代码,保护源代码。

2. 动态库

2.1 动态库又名共享库,在程序运行时才被动态的加载到程序内存空间中,不占用程序的空间和资源,但加载速度相较静态库更慢。
2.2 更新部署时不需要重新编译全部程序,只更新相应的动态库即可。
2.3 导入库:动态库的 lib 文件叫导入库,和静态库不同,导入库仅包含地址符号表,用来映射动态库中的函数、变量。Linux 动态库没有导入库。

2.4 Windows 动态库的后缀名为 .lib,Linux 动态库的格式为 libXXX.so

二 动态库

1. QtCreator 向导创建动态库 

1. 点击创建项目,选择:库 ->C++ Library

2. 输入项目名称和项目创建路径

3. 选择构建工具:CMake

4. 在定义工程详情界面,选择库的类型为:Shared Library(动态库)、选择用到的 Qt 模块、动态库类名、头文件和源文件
    补充:Qt 模块有三个:Core、Gui、Widget

 4.1 Core (QtCore)是 Qt 的基础模块,提供了基本的数据结构、字符串处理、时间日期处理、文件和目录操作、流处理、国际化(i18n)支持、事件处理、线程管理、定时器、插件系统等核心功能。

 4.2 Gui (QtGui)模块构建在 QtCore 之上,提供了图形用户界面的基础元素,包括颜色、字体、图标、绘图、2D 图形渲染、图像处理、窗口系统集成、事件处理(针对鼠标、键盘等输入设备)、以及基本的用户界面控件(如按钮、标签等),但不包括复杂的界面部件(如对话框、布局管理器等)。

 4.3 Widgets (QtWidgets)模块是基于 QtGui 的更高层次模块,它包含了大量预建的用户界面部件(Widgets),如按钮(Button)、标签(Label)、文本框(LineEdit)、列表视图(ListView)、表格视图(TableWidget)等。

5. 选择翻译语言、编译套件、代码版本管理等

6. 动态库创建完成

2. CMakeLists.txt 设置动态库的生成路径

下面的代码区分了Windows和Linux、Debug模式和Release模式

#设置 Windows 程序的生成路径
#CMAKE_CURRENT_LIST_DIR-当前正在执行的 CMakeLists.txt 文件所在目录
set(WindowsPath  ${CMAKE_CURRENT_LIST_DIR}/../Windows)
#设置 Linux 程序的生成路径
set(LinuxPath  ${CMAKE_CURRENT_LIST_DIR}/../Linux)

#指定动态库的生成路径
#注意:设置动态库的生成路径应该在任何 add_library 命令之前进行
if(WIN32)

  # Windows 系统
  if (${CMAKE_BUILD_TYPE} STREQUAL "Debug")

    #1. Debug 模式
    #1.1 设置动态库导入库的输出目录(Windows)
    set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${WindowsPath}/debug/lib")
    #1.2 设置动态库(.dll 或 .so)的输出目录
    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${WindowsPath}/debug/bin")

    #创建目录
    file(MAKE_DIRECTORY "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG}")
    file(MAKE_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG}")

  elseif(${CMAKE_BUILD_TYPE} STREQUAL "Release")

    #2. Release 模式
    #1.1 设置动态库导入库的输出目录(Windows)
    set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${WindowsPath}/release/lib")
    #1.2 设置动态库(.dll 或 .so)的输出目录
    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${WindowsPath}/release/bin")

    #创建目录
    file(MAKE_DIRECTORY "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE}")
    file(MAKE_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE}")

  endif()

elseif(UNIX AND NOT APPLE)

  # Unix 系统但不是 Apple 系统
   if (${CMAKE_BUILD_TYPE} STREQUAL "Debug")

      #1. Debug 模式
      #1.1 设置动态库(.so)的输出目录
      set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${LinuxPath}/debug/bin")
      #创建目录
      file(MAKE_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG}")

  elseif(${CMAKE_BUILD_TYPE} STREQUAL "Release")

      #2. Release 模式
      #1.1 设置动态库(.so)的输出目录
      set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${LinuxPath}/release/bin")
      #创建目录
      file(MAKE_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE}")

  endif()

endif()

3. 导入[导出]类、导入[导出]函数 

3.1 利用 QtCreator 的向导创建动态库后,会自动生成一段定义宏的代码,用来区分导入导出函数,代码如下:

#if defined(CREATORDYNAMICLIB_LIBRARY)
#define CREATORDYNAMICLIB_EXPORT Q_DECL_EXPORT
#else
#define CREATORDYNAMICLIB_EXPORT Q_DECL_IMPORT
#endif

3.2 导入[导出]函数

//需要使用 DYNLOADDLL_EXPORT 声明
//需要使用 extern "C" 修饰,防止函数重载导出导出函数名称改变,从而无法定位导出函数的问题
extern "C"
{
    //导出函数
    void DYNLOADDLL_EXPORT DynFun(QString strVal);
}

3.3 导入[导出]类

//如果导出类的成员函数为信号或槽函数,则导出类需要派生于 QObject,且在类内声明宏Q_OBJECT
class DYNLOADDLL_EXPORT DynLoadDll : public QObject
{
    Q_OBJECT
public:
    DynLoadDll();
};

导入函数、导出函数的区别 

1. 导入函数是动态库内部声明的函数,供动态库自身使用;导出函数是动态库为外部提供的函数,提供给使用动态库的源程序使用。

2. 通常在动态库内部声明一个宏,根据这个宏是否定义来区分导入导出函数,如下:

//动态库中定义这个宏 CREATORDYNAMICLIB_LIBRARY,这样,对于同一份函数声明文件(.h),从动态库内部访问,这个函数就是导入函数,从动态库外部访问,这个函数就是导出函数。
#if defined(CREATORDYNAMICLIB_LIBRARY)
#define CREATORDYNAMICLIB_EXPORT Q_DECL_EXPORT
#else
#define CREATORDYNAMICLIB_EXPORT Q_DECL_IMPORT
#endif

4. 隐式链接使用动态库

4.1 需要头文件、动态库文件,如果是Windows动态库,还需要导入库文件(.a 或 .lib)

4.2 通过CMakeLists.txt隐式链接动态库,下面的代码区分了Windows和Linux、Debug模式和Release模式

#设置当前项目的名称(使用动态库的项目)
set(ProjectName  UserDemo)
#指定动态库头文件所在路径
#CMAKE_CURRENT_LIST_DIR-当前正在执行的 CMakeLists.txt 文件所在目录
set(SharedLibHeadFilePath ${CMAKE_CURRENT_LIST_DIR}/../GridControl)
#设置 Windows 程序的生成路径
#CMAKE_CURRENT_LIST_DIR-当前正在执行的 CMakeLists.txt 文件所在目录
set(WindowsPath  ${CMAKE_CURRENT_LIST_DIR}/../Windows)
#设置 Linux 程序的生成路径
set(LinuxPath  ${CMAKE_CURRENT_LIST_DIR}/../Linux)
#设置动态库文件名称,Windows-导入库文件名称  Linux-动态库文件名称
set(WindowsSharedLibName  libGridControl.dll.a)
set(LinuxSharedLibName  libGridControl.so)


#1. 指定动态库的头文件的搜索路径
#注意:指定库的头文件所在路径要在生成使用动态库的项目之前

# 指定库的头文件的搜索路径
include_directories(${SharedLibHeadFilePath})
# 输出变量 SharedLibHeadFilePath 的值
message(STATUS "head path1234567890: ${SharedLibHeadFilePath}")


#2. 指定可执行程序的生成路径
#注意:设置可执行程序的生成路径要在任何 add_library 命令之前进行
if(WIN32)

  # Windows 系统
  if (${CMAKE_BUILD_TYPE} STREQUAL "Debug")

    #Debug 模式
    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${WindowsPath}/debug/bin")
    #创建目录
    file(MAKE_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG}")

  elseif(${CMAKE_BUILD_TYPE} STREQUAL "Release")

    #Release 模式
    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${WindowsPath}/release/bin")
    #创建目录
    file(MAKE_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE}")

  endif()

elseif(UNIX AND NOT APPLE)

  # Unix 系统但不是 Apple 系统
   if (${CMAKE_BUILD_TYPE} STREQUAL "Debug")

      #Debug 模式
      set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${LinuxPath}/debug/bin")
      #创建目录
      file(MAKE_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG}")

  elseif(${CMAKE_BUILD_TYPE} STREQUAL "Release")

      #Release 模式
      set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${LinuxPath}/release/bin")
      #创建目录
      file(MAKE_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE}")

  endif()

endif()


#生成使用动态库的可执行程序......


#3. 隐式链接动态库
# 注意:隐式链接动态库要在生成使用动态库的项目之后
if(WIN32)

  # Windows 系统
  if (${CMAKE_BUILD_TYPE} STREQUAL "Debug")

    #Debug 模式

    #3.1 指定库文件所在路径[不起作用]
    #link_directories(${CMAKE_BINARY_DIR}/${WindowsPath}/debug/lib)

    #3.2 遍历库文件列表,链接每个库文件
    foreach(lib_name IN LISTS WindowsSharedLibName)

        # 构建完整的库文件路径
        set(full_lib_path "${WindowsPath}/debug/lib/${lib_name}")

        #隐式链接动态库
        #参数1:要使用动态库的项目名称
        #参数2:可选,作用范围
        #参数3:Windows-导入库的名称或者导入库的名称 + 后缀名; Linux-动态库的名称或者动态库的名称 + 后缀名
        #注意1:所有对同一目标的 target_link_libraries 调用,参数2要一致
        #注意2:参数3需要指定库文件的完整路径,提前使用 link_directories 指定库文件路径 + 参数3仅传入库文件名称 的方法不起作用
        target_link_libraries(${ProjectName} PRIVATE ${full_lib_path})
    endforeach()

  elseif(${CMAKE_BUILD_TYPE} STREQUAL "Release")

    #Release 模式
    #遍历库文件列表,链接每个库文件
    foreach(lib_name IN LISTS WindowsSharedLibName)
        set(full_lib_path "${WindowsPath}/release/lib/${lib_name}")
        target_link_libraries(${ProjectName} PRIVATE ${full_lib_path})
    endforeach()

  endif()

elseif(UNIX AND NOT APPLE)

  # Unix 系统但不是 Apple 系统
   if (${CMAKE_BUILD_TYPE} STREQUAL "Debug")

      #Debug 模式
      #遍历库文件列表,链接每个库文件
      foreach(lib_name IN LISTS LinuxSharedLibName)
          set(full_lib_path "${CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG}/${lib_name}")
          target_link_libraries(${ProjectName} PRIVATE ${full_lib_path})
      endforeach()

  elseif(${CMAKE_BUILD_TYPE} STREQUAL "Release")

      #Release 模式
      #遍历库文件列表,链接每个库文件
      foreach(lib_name IN LISTS LinuxSharedLibName)
          set(full_lib_path "${CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE}/${lib_name}")
          target_link_libraries(${ProjectName} PRIVATE ${full_lib_path})
      endforeach()

  endif()

endif()

5. 显示链接使用动态库

5.1 显示链接需要头文件、动态库文件

5.2 QLibrary

5.2.1 功能

1. 加载和卸载动态库
2. 解析动态库的导出函数
3. 不直接支持 Qt 的元对象系统特性,因此不适合直接加载和实例化派生于 QObject 的导出类。

5.2.2 API

1. 构造函数,构造一个 QLibrary 对象并指定库文件名
QLibrary::*QLibrary(const QString &fileName, QObject parent = nullptr);

2. 加载库
//返回值:成功返回 true,失败返回 false;可通过 errorString() 获取错误信息
bool QLibrary::load();

3. 卸载已加载的库
//返回值:成功返回 true,失败返回 false;可通过 errorString() 获取错误信息
bool QLibrary::unload();

4. 检查库是否已经被成功加载
bool QLibrary::isLoaded() const;

5. 返回最后一次操作失败时的错误信息
QString QLibrary::errorString() const;

6. 根据符号名称解析库中的函数地址
//参数:要解析的函数名称
//返回值:函数指针类型,成功返回函数地址,失败返回 nullptr
FARPROC QLibrary::resolve(const QString &symbol);

7. 返回库文件的完整路径
QString QLibrary::fileName() const;

8. 返回库的版本信息(如果可用)
QString QLibrary::version() const;

5.3 代码示例

    //1. 加载动态库
    QLibrary myLibrary("libDynLoadDll.dll");
    if (!myLibrary.load())
    {
        qDebug() << "加载动态库失败,错误信息:" << myLibrary.errorString();
        return -1;
    }

    //2. 获取导出函数
    void (*myFun)(QString strVal) = (void (*)(QString))(myLibrary.resolve("DynFun"));
    if (!myFun)
    {
        qDebug() << "获取动态库中的导出函数失败!";
        return -1;
    }

    //3. 执行导出函数
    myFun("显示链接动态库,执行导出函数成功!");

标签:set,CMAKE,Qt,lib,DIRECTORY,OUTPUT,动态
From: https://blog.csdn.net/qq_36318563/article/details/140418430

相关文章

  • Qt-线程和线程池
    前言Qt实现在线程中执行任务有4种方法,分别是:1. 创建一个派生于QThread类的子类,重写run函数,在run函数中执行任务2.创建一个派生于QObject的子类,调用QObject::moveToThread()方法将子类对象移动到子线程对象中。3.使用线程池QThreadPool4.使用QtConcurrent执行并......
  • lib与dll(静态库与动态库)
    在计算机软件开发中,lib和dll是两种不同的库文件类型,用于代码共享和模块化。以下是它们的详细解释:静态库(StaticLibrary-.lib)特点文件扩展名:通常为.lib(在Windows上)或.a(在Unix/Linux系统上)。编译时间链接:静态库在编译时被直接链接到目标应用程序中。这意味着在......
  • 动态数组
    vector类说明:不强制类实际元素具有唯一性的集合;基本上是个数组,可以调大小扩大数组的过程:开始默认开辟一个单元大小,当扩大时,在内存上开辟一块空间比刚开始的大,再把之前的复制过来,再删掉旧的数组;基本使用代码示例:intmain(){std::vector<vertex>a;//把vertex存在一段内存......
  • 阶段三:项目开发---民航功能模块实现:任务16:动态航线图
    任务描述内 容: 前面任务实现了由SparkStreaming实时接收Kafka中的数据进行清洗,并存入到了MySQL中,有了飞机的实时经纬度坐标等数据,接下来,开发前台页面并接入百度地图模块,实现飞机的动态航线图功能。学 时:6学时知识点:动态航线图功能实现,ECharts、Vue和iView熟悉重点:熟......
  • C++客户端Qt开发——开发环境
    一、QT开发环境1.安装三个部分①C++编译器(gcc,cl.exe……)②QTSDKSDK-->软件开发工具包比如,windows版本QTSDK里已经内置了C++的编译器(内置编译器是mingw,windows版本的gcc/g++)③QT的集成开发环境(IDE)官方提供的QTCreator最容易入门,最容易上手的方式,开箱即用,虽然QTCrea......
  • 数据结构,(动态)顺序表,C语言实现
    ——如果代码存在问题,请务必评论告诉我,感激不尽(#^.^#)——动态和静态的顺序表差别主要在于开辟内存的方式,动态顺序表中的数据所在内存是通过malloc函数实现的,这也意味着,动态顺序表可以更改存储数据的内存大小,其他的话基本没什么差别1.数据类型定义 structElemType想要建......
  • 【Qt 信号和槽】一篇文章带你详细的了解 Qt 的信号与槽
    文章目录1.信号和槽的基本概念2.`connect()`函数的用法......
  • Qt-事件过滤器、事件分发器、事件处理器
    前言Qt中事件的处理步骤1.当事件产生之后,Qt应用程序对象通过调用QApplication::notify()函数将事件发送到指定的窗口。2.事件在发送过程中可以通过向对象(窗口、按钮等)安装事件过滤器QObject::eventFilter()来对事件进行过滤。Qt应用程序默认不对任何产生的事件......
  • 动态规划的“三步走”方法
    “三步走”方法动态规划问题种类较多,但大多都能通过“三步走”方法解决。状态表示:将具体问题抽象为数学问题,明确需要表示的状态,数组中的下标分别表示哪种状态。状态转移:状态转移相当于递推公式。主要有两种方式,可以从上一个状态转移到当前状态,或者从当前状态转移到下一个状态......
  • MQTT是什么,物联网
    写文思路:以下从几个方面介绍MQTT,包括:MQTT是什么,MQTT和webSocket的结合,以及使用场景,一、MQTT是什么MQTT(MessageQueuingTelemetryTransport)是一种轻量级的发布/订阅消息传输协议,广泛应用于物联网(IoT)设备之间的通信。MQTT在设计时考虑了低带宽、不可靠网络环境下的高效......