1 库介绍
先对库进行介绍,后对Qt静态链接库(.lib .a)、动态链接库(.dll)进行创建和使用的介绍!
库是什么
库是写好的现有的,成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。库有两种:静态库(.a、.lib)和动态库(.so、.dll)。
所谓静态、动态是指链接。回顾一下,将一个程序编译成可执行程序的步骤:
静态库是什么
之所以成为【静态库】,是因为在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接。
试想一下,静态库与汇编生成的目标文件一起链接为可执行文件,那么静态库必定跟.o文件格式相似。其实一个静态库可以简单看成是一组目标文件(.o/.obj文件)的集合,即很多目标文件经过压缩打包后形成的一个文件。
静态库特点总结:
l 静态库对函数库的链接是放在编译时期完成的。
l 程序在运行时与函数库再无瓜葛,移植方便。
l 浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。
Linux下使用ar工具、Windows下vs使用lib.exe,将目标文件压缩到一起,并且对其进行编号和索引,以便于查找和检索。一般创建静态库的步骤如图所示:
动态库是什么
通过上面的介绍发现静态库,容易使用和理解,也达到了代码复用的目的,那为什么还需要动态库呢?为什么还需要动态库?为什么需要动态库,其实也是静态库的特点导致。空间浪费是静态库的一个问题。
另一个问题是静态库对程序的更新、部署和发布页会带来麻烦。如果静态库liba.lib更新了,所以使用它的应用程序都需要重新编译、发布给用户(对于玩家来说,可能是一个很小的改动,却导致整个程序重新下载,全量更新)。
动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新。
动态库特点总结:
l 动态库把对一些库函数的链接载入推迟到程序运行的时期。
l 可以实现进程之间的资源共享。(因此动态库也称为共享库)
l 将一些程序升级变得简单。
l 甚至可以真正做到链接载入完全由程序员在程序代码中控制(显示调用)。
Window与Linux执行文件格式不同,在创建动态库的时候有一些差异。
l 在Windows系统下的执行文件格式是PE格式,动态库需要一个DllMain函数做出初始化的入口,通常在导出函数的声明时需要有_declspec(dllexport)关键字。
l Linux下gcc编译的执行文件默认是ELF格式,不需要初始化入口,亦不需要函数做特别的声明,编写比较方便。
与创建静态库不同的是,不需要打包工具(ar、lib.exe),直接使用编译器即可创建动态库。
2 静态链接库(.lib,.a)创建
2.1 使用说明
1.QT创建一个静态链接库项目,设计各种需要导出的类,包括具有UI的窗体类、对话框类、编译后可以生成一个lib文件(MSVC编译器生成的文件后缀为“.lib”,MinGW编译器生成的文件后缀为“.a”)
2.在另一个应用程序里使用这个lib文件和类的头文件(不需要cpp源文件),就可以静态编译到应用程序里
3.这种方法适合于小组开发时,每个人负责自己的部分,使用其他人设计的代码时只能使用而不能看到源码,便于项目代码的管理
2.2 静态链接库.lib工程创建流程
(1)点击“文件”-》“新建文件或项目”,选择“Library”中的“C++ 库”
(2)在类型中,选择“静态链接库”,设置项目名称为“myStaticLib”
(3)选择的是编译器的版本,即构建套件,有些电脑安装多个版本,请选择一个合适的静态编译版本套件进行编译!
(4)选择需要包含的QT模块。
(5)类定义页面,定义类名
(6)完成库工程创建
2.3 静态链接库.lib库文件代码加入
(1)静态库文件的功能需要自己去设计,可以只实现一个函数功能,也可以设计一个带有UI界面的应用,在此实现一个简单的函数功能进行演示。
mystaticlib.cpp文件内容:
#include "mystaticlib.h"#include <QDebug>MyStaticLib::MyStaticLib(){}uint32_t MyStaticLib::MyAdd(uint32_t a,uint32_t b){ uint32_t sum = 0; sum = a + b; qDebug("Release, MyAdd: %d + %d = %d\n",a,b,sum); return sum;}uint32_t MyStaticLib::MySub(uint32_t a,uint32_t b){ uint32_t diff = 0; diff = a + b; qDebug("Release,MySub: %d + %d = %d\n",a,b,diff); return diff;}
mystaticlib.h文件内容:
#ifndef MYSTATICLIB_H#define MYSTATICLIB_Hclass MyStaticLib{public: MyStaticLib(); uint32_t MyAdd(uint32_t a,uint32_t b); uint32_t MySub(uint32_t a,uint32_t b);};#endif // MYSTATICLIB_H
(2)项目文件(.pro)的内容介绍如下(为自动生成的)
TEMPLATE定义项目模板是库,而不是应用程序CONFIG:配置项目为静态库TARGET:定义项目编译后生成的目标文件名称为myStaticLib
(3)将项目在编译器版本下的debug和release模式下分别编译(右键项目,点击“qmake”,再点击“构建”),注意静态编译库工程不会生成可执行程序。
点击“构建”
在编译器版本下的debug和release模式下分别编译(右键项目,点击“qmake”,再点击“构建”)后,在对应的的build目录下会生成.lib文件,这个就是我们最终生成的静态库文件(注意debug版本的lib文件不会再文件名后加一个字符“d”)
3 静态链接文件的使用
3.1 新建项目工程(要调用lib库的工程)
(1)新建工程
1. 新建一个名为“lib_user”的项目,项目是一个基于QMainWindows的应用程序
2. 在项目源程序目录下新建一个include目录(这个目录名称自己设计)
(2)复制库文件到项目工程
然后根据使用的编译器复制不同的文件
1. 若使用MSVC编译器:将静态库项目myStaticLib下的mystaticlib.h、release版本的mystaticlib.lib复制到这个include目录下、将debug版本的mystaticlib.lib更名为myStaticLibd.lib复制到这个include目录下,以便于程序在debug和release模式下编译时调用特定类型的文件
2. 若使用MinGW编译器:就将mystaticlib.a和mystaticlib.a(debug版本)复制到include目录里
复制到include的文件如下
3.2 项目工程添加.lib库
(1)项目名处鼠标右键,点击“添加库...”
(2)选择外部库
(3)选择要添加的库文件,并选择平台(可以全选),链接类型(为静态),勾选"为debug版本添加"d"作为后缀”(.pro会自动区分是debug还是release版本)
(4)添加库后自动生成的.pro
3.3 项目中使用.lib库的函数接口
(1)在项目文件中添加我们刚才添加的静态库头文件,这样才可以使用里面的内容;同时定义函数类的成员进行使用;
(2)项目实体函数中,调用lib库函数类成员函数进行使用,如图调用lib库的加法和减法函数进行运算;
3.4 编译运行带.lib的库项目工程
(1)在Debug模式下,进行“qmake”->“构建”-》“运行”,如下,运行的是Debug模式下编译的“myStaticLibd.lib”库函数接口(接口的打印加了Debug)
(2)在Release模式下,进行“qmake”->“构建”-》“运行”,如下,运行的是Release模式下编译的“myStaticLib.lib”库函数接口(接口的打印加了Release)
到此.lib库的创建和使用流程介绍完毕,上面介绍的只实现一个简单函数功能的库,也可以设计一个复杂的函数功能或者带有UI界面库。