Qt 静态库和共享库创建以及使用
一、程序编译过程
二、静态库创建和使用
1、新建Mainwindow项目,写一个定时器做测试
a).h
b).cpp
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include "staticclass.h" 2 #include "ui_staticclass.h" 3 #include<QTimer> 4 5 staticClass::staticClass(QWidget *parent) 6 : QMainWindow(parent) 7 , ui(new Ui::staticClass) 8 { 9 ui->setupUi(this); 10 steps = 0; 11 12 timer = new QTimer(); 13 connect(timer, &QTimer::timeout, this, &staticClass::updateTime); 14 } 15 16 staticClass::~staticClass() 17 { 18 if(timer) 19 { 20 delete timer; 21 timer = nullptr; 22 } 23 delete ui; 24 } 25 26 27 void staticClass::on_btnStart_clicked() 28 { 29 timer->start(500); 30 } 31 32 void staticClass::updateTime() 33 { 34 ++ steps; 35 ui->lcdNumber->display(QString::number(steps)); 36 } 37 38 void staticClass::on_btnStop_clicked() 39 { 40 timer->stop(); 41 }
c)运行
2、生成静态库
a)创建库项目,注意选择库选项,别选成共享库了
b)将生成的定时器.h/.cpp/.ui文件拷贝至库项目文件夹内,替换掉它的.h/.cpp文件,并加载.ui文件
c)修改.pro文件,因为使用了QMainwindow类,属于widgets
在.pro文件中添加 QT += widgets
d)分别构建库项目的Debug文件夹和Release文件夹,并把两个文件中的.lib文件拷贝出来,其中Debug文件夹的.lib记得在末尾添加d
例如:staticClass.lib 改成staticClassd.lib,最后再把库文件夹的.h(其实也是定时器的.h)拷贝出来,总共3个文件
3、使用静态库
a)创建新的QMainwindow项目,ui界面中拖拽一个widget框,在项目目录下将上一步的2个.lib文件和.h放入新建文件夹lib
b)添加静态库
.lib选第一个即可,最后.pro新增如下
c)修改QMainwindow的.h文件
d)修改QMainwindow的.cpp
e)运行
三、共享库生成和隐式链接调用
1、生成共享库
a)生成库项目,Type选择Shared Library
b)将定时器的.h/.cpp/.ui拷贝至库目录,加载进去(不要在意类名,定时器项目请看静态库第一步)
c).pro增加QT += widgets,复制库目录原生的.h中的宏(该宏来自xxx_global.h),粘贴至定时器的.h
d)库目录.h文件添加QString Str() 函数成员,用于做测试(不做测试可以把库文件的.h和.cpp删掉,但是必须保留库_global.h)
e)分别构建库项目的Debug文件夹和Release文件夹,并把两个文件中的.lib和dll文件拷贝出来,其中Debug文件夹的.lib记得在末尾添加d
例如:staticClass.lib 改成staticClassd.lib,最后再把库文件夹的两个.h和定时器的.h)拷贝出来,总共7个文件(当然,也可以把库文件.h删掉)。
PS:也可以将库_global.h定义代码拷贝出来,放到其他.h文件中,那就能减少一个文件,这里依旧留下。
xxxd.lib(Debug)
xxx.lib(Release)
xxx.dll(Debug)
xxx.dll(Release)
库.h
库_global.h
定时器.h
2、使用共享库
a)创建新的QMainwindow项目,ui界面中拖拽一个widget框,在项目目录下将上一步的.lib文件和.h放入新建文件夹ShareLibImplicit(共5个)
b)添加动态库
.lib选第一个即可,最后.pro新增如下
c)修改QMainwindow.cpp(头文件不需要添加XXX_global.h)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include "mainwindow.h" 2 #include "ui_mainwindow.h" 3 #include<QHBoxLayout> 4 #include"ShareLibImplicit/sharedlib_implicit.h" // 库测试类头文件 5 #include"ShareLibImplicit/staticclass.h" // 定时器类头文件 6 #include<QDebug> 7 8 MainWindow::MainWindow(QWidget *parent) 9 : QMainWindow(parent) 10 , ui(new Ui::MainWindow) 11 { 12 ui->setupUi(this); 13 staticClass * sc = new staticClass(this); // 定时器类 14 QHBoxLayout *layout = new QHBoxLayout(ui->widget); 15 layout->addWidget(sc); 16 ui->widget->setLayout(layout); 17 18 SharedLib_implicit s; // 库测试类 19 qDebug()<< s.Str(); 20 } 21 22 MainWindow::~MainWindow() 23 { 24 delete ui; 25 }
d)构建项目Debug和Release文件夹,并将
xxx.dll(Debug)
xxx.dll(Release)
分别拷贝至对应的Debug和Release文件夹中
e)运行,可以看到定时器类生成成功,库测试类也打印成功
四、共享库生成和显示链接调用
1、生成共享库(参考三的生成共享库)
a)一样
b)一样
c)一样
d)修改文件,其中sharedlib_implicit为库文件,staticclass为定时器文件
修改sharedlib_implicit.h,其中
Q_DECL_EXPORT 代表输出
Q_DECL_IMPORT 代表输入
extern 全局声明不能在类内部使用
extern "C" Q_DECL_EXPORT 生成一个连接外部的函数链接
Q_DECL_EXPORT和Q_DECL_IMPORT程序会根据调用情况,选择其中一个来替代。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #ifndef SHAREDLIB_IMPLICIT_H 2 #define SHAREDLIB_IMPLICIT_H 3 4 #include<QWidget> 5 6 /** @details 将global.h文件内容拷贝过来*/ 7 #if defined(SHAREDLIB_IMPLICIT_LIBRARY) 8 # define SHAREDLIB_IMPLICIT_EXPORT Q_DECL_EXPORT 9 #else 10 # define SHAREDLIB_IMPLICIT_EXPORT Q_DECL_IMPORT 11 #endif 12 13 class SHAREDLIB_IMPLICIT_EXPORT SharedLib_implicit 14 { 15 public: 16 SharedLib_implicit(); 17 QString Str(); 18 }; 19 20 extern "C" Q_DECL_EXPORT int add(int a, int b); 21 #endif // SHAREDLIB_IMPLICIT_H
修改sharedlib_implicit.cpp
修改staticclass.h,创建一个函数和虚函数,用于测试,再创建一个外部链接函数,用于调用定时器函数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #ifndef STATICCLASS_H 2 #define STATICCLASS_H 3 4 #include <QMainWindow> 5 6 #if defined(SHAREDLIB_IMPLICIT_LIBRARY) 7 # define SHAREDLIB_IMPLICIT_EXPORT Q_DECL_EXPORT 8 #else 9 # define SHAREDLIB_IMPLICIT_EXPORT Q_DECL_IMPORT 10 #endif 11 12 QT_BEGIN_NAMESPACE 13 namespace Ui { class staticClass; } 14 QT_END_NAMESPACE 15 16 class SHAREDLIB_IMPLICIT_EXPORT staticClass : public QMainWindow 17 { 18 Q_OBJECT 19 20 public: 21 staticClass(QWidget *parent = nullptr); 22 ~staticClass(); 23 int mul1(int a); 24 virtual int mul2(int a); // 虚函数 25 26 27 private slots: 28 void on_btnStart_clicked(); 29 void on_btnStop_clicked(); 30 31 private: 32 void updateTime(); 33 34 private: 35 Ui::staticClass *ui; 36 QTimer * timer; 37 int steps; 38 }; 39 40 extern "C" Q_DECL_EXPORT staticClass * createStaticClass(QWidget *parent = nullptr); 41 #endif // STATICCLASS_H
修改staticclass.cpp
e)分别构建库项目的Debug文件夹和Release文件夹,并把两个文件中dll文件拷贝出来,
再把staticclass.h拷贝出来,共3个文件,这里可能会有疑问,为什么不拷贝sharedlib_implicit.h,
等下会解答
2、使用共享库
a)创建新的QMainwindow项目,ui界面中拖拽一个widget框和一个按钮,构项目的Debug文件夹和Release文件夹,
把dll分别放入对应文件夹。把staticclass.h放入QMainwindow项目根目录
b)mainwindow.cpp添加头文件staticclass.h,按钮槽函数添加代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include "mainwindow.h" 2 #include "ui_mainwindow.h" 3 #include<QHBoxLayout> 4 #include<QDebug> 5 #include <QLibrary> 6 #include"staticclass.h" 7 8 MainWindow::MainWindow(QWidget *parent) 9 : QMainWindow(parent) 10 , ui(new Ui::MainWindow) 11 { 12 ui->setupUi(this); 13 } 14 15 MainWindow::~MainWindow() 16 { 17 delete ui; 18 } 19 20 21 void MainWindow::on_pushButton_clicked() 22 { 23 typedef int (*Fun1)(int, int); // 定义函数指针 24 typedef staticClass* (*Fun2)(QWidget*); 25 QLibrary myLib("sharedLib_implicit"); // 加载dll文件,可以不写后缀dll 26 27 28 if(myLib.load()) 29 { 30 // sharedLib_implicit.h的add函数 31 Fun1 num = (Fun1)myLib.resolve("add"); 32 int n = num(3 ,4); 33 qDebug()<<QString::number(n); 34 35 // staticClass类调用 36 Fun2 s = (Fun2)myLib.resolve("createStaticClass"); 37 staticClass* sc = s(this); 38 QHBoxLayout * layout = new QHBoxLayout(ui->widget); 39 layout->addWidget(sc); 40 ui->widget->setLayout(layout); 41 42 // mul1报错,因为没有使用虚函数 43 // int m1 = sc->mul1(5); 44 // qDebug()<<m1; 45 int m2 = sc->mul2(5); 46 qDebug()<<m2; 47 } 48 }
c)运行效果
d)为什么要添加定时器的头文件staticclss.h,而库文件的头文件不需要添加sharedlib_implicit.h?
因为库文件里面没有使用到sharedlib_implicit的成员,只用到一个全局函数,
而定时器staticclss类使用了成员函数,为了解析成员函数的结构和关系,必须用到staticclss.h。
e)为什么只有虚函数mul2不报错,函数mul1报错?
因为成员函数非虚时,其地址在程序编译时候就确定了,而共享库是动态链接的,无法知道其静态地址。
成员函数是虚函数时,程序编译会生成虚函数表,虚函数表每个元素都指向其中一个虚函数地址,虚函数表
是编译时地址就确定了,但是虚函数地址是运行时候确定的,即程序运行到虚函数才给虚函数分配地址。
五、静态库和共享库优缺点(转自:https://www.cnblogs.com/renzhuang/p/6661519.html)
1 静态链接库的优点
(1) 代码装载速度快,执行速度略比动态链接库快;
(2) 只需保证在开发者的计算机中有正确的.LIB文件,在以二进制形式发布程序时不需考虑在用户的计算机上.LIB文件是否存在及版本问题。
2 动态链接库的优点
(1) 更加节省内存并减少页面交换;
(2) DLL文件与EXE文件独立,只要输出接口不变(即名称、参数、返回值类型和调用约定不变),更换DLL文件不会对EXE文件造成任何影响,因而极大地提高了可维护性和可扩展性;
(3) 不同编程语言编写的程序只要按照函数调用约定就可以调用同一个DLL函数;
(4)适用于大规模的软件开发,使开发过程独立、耦合度小,便于不同开发者和开发组织之间进行开发和测试。
3 不足之处
(1) 使用静态链接生成的可执行文件体积较大,包含相同的公共代码,造成浪费;
(2) 使用动态链接库的应用程序不是自完备的,它依赖的DLL模块也要存在,如果使用载入时动态链接,程序启动时发现DLL不存在,系统将终止程序并给出错误信息。而使用运行时动态链接,系统不会终止,但由于DLL中的导出函数不可用,程序会加载失败;速度比静态链接慢。当某个模块更新后,如果新模块与旧的模块不兼容,那么那些需要该模块才能运行的软件,统统撕掉。这在早期Windows中很常见。
六、总结
1、静态库需要.lib 和.h,Debug生成的.lib需要加d,.lib包含了.h和.cpp的链接关系及.cpp的内容。
2、共享库隐式链接调用需要.lib,.dll,.h文件。
3、共享库显然式链接调用需要.dll文件,.h文件看情况而定。显示调用类内成员函数,只能调用虚函数,因为
显示调用只能获得动态地址。
4、DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数,为动态链接提供了一种方法,
DLL 还有助于共享数据和资源。多个应用程序可同时访问内存中单个DLL 副本的内容。DLL 是一个包含可由
多个程序同时使用的代码和数据的库。