前言
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、Widget4.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