目录
验证环境:Win10+Qt5.14
验证内容:Qt导出类。
1、建立并生成动态链接库
1、打开QtCreator,新建工程,我们选择Library,C++库,然后就是choose。
2、然后在弹出的C++库对话框我们默认类型共享库,名称为MyDll。然后,一直下一步,直到点击完成。
3、生成的代码结构
MyDll.pro
QT -= gui # 不需要界面
TARGET = MyDll #工程名
TEMPLATE = lib # 生成lib
DEFINES += MYDLL_LIBRARY # 预定义宏
DEFINES += QT_DEPRECATED_WARNINGS
SOURCES += \
mydll.cpp
HEADERS += \
mydll.h \
mydll_global.h
unix {
target.path = /usr/lib
INSTALLS += target
}
mydll_global.h中,内容如下:
#ifndef MYDLL_GLOBAL_H
#define MYDLL_GLOBAL_H
#include <QtCore/qglobal.h>
#if defined(MYDLL_LIBRARY)
# define MYDLLSHARED_EXPORT Q_DECL_EXPORT
#else
# define MYDLLSHARED_EXPORT Q_DECL_IMPORT
#endif
#endif // MYDLL_GLOBAL_H
可以看到系统自动定义了两个宏。用于声明哪些函数是需要我们导出的。
2、导出类工程目录
以工程导出mydll.dll为例。Mydll类中包含ClassA、ClassB两个类的支持,并在Mydll类的接口中需要引用ClassA/ClassB中定义的结构体structA/structB。如果将两个Class定义在mydll.h中,提供动态库时则必须同时提供这两个类的头文件,比较繁琐,也不利于封装。
好的做法是:
1)将ClassA、ClassB及其它更多类的支持封装在一个公共类中,在接口类中定义该公共类指针,用于ClassA、ClassB类对象的访问;
2)将需要暴露出来的结构体,进行整体封装,写入mydll.h头文件中,之后在mydll.cpp的函数体内进行解析为对应的结构体。或者将公共部分抽象为公共头文件。
具体实践:
classa.h定义,定义结构体structA,并使用其进行初始化。
#ifndef CLASSA_H
#define CLASSA_H
#include <iostream>
struct structA
{
structA() {}
int a;
};
class ClassA
{
public:
ClassA();
void init(structA &a) { std::cout<<"ClassA init."<<std::endl; }
void run() { std::cout<<"ClassA run()."<<std::endl; }
};
#endif // CLASSA_H
classb.h定义,定义结构体structB,并使用其进行初始化。
#ifndef CLASSB_H
#define CLASSB_H
#include <iostream>
struct structB
{
structB() {}
float b;
};
class ClassB
{
public:
ClassB();
void init(structB &b) { std::cout<<"ClassB init."<<std::endl; }
void run() { std::cout<<"ClassB run()."<<std::endl; }
};
#endif // CLASSB_H
接口类定义:
1)接口类头文件定义,mydll.h:
#ifndef MYDLL_H
#define MYDLL_H
#include "mydll_global.h"
struct structC //公共头文件,封装所需参数,对外接口
{
structC() {}
int a;
};
class MYDLLSHARED_EXPORT Mydll
{
public:
Mydll();
~Mydll();
int init(structC &a); // 输出公共结构体参数,函数内解析适配ClassA、ClassB。
int run();
private:
// 声明封装类DetImp,并定义该类指针_detImp,此时DetImp类并未定义。
class DetImp;
DetImp *_detImp;
};
#endif // MYDLL_H
2)接口类源文件定义,mydll.cpp:
#include "mydll.h"
#include "classa.h" // .cpp中引用classa.h,不必暴露在接口中
#include "classb.h"
#include <iostream>
class Mydll::DetImp // 接口类中DetImp类实现,只做其它类封装。
{
public:
DetImp(){}
~DetImp(){}
ClassA m_clsa;
ClassB m_clsb;
};
Mydll::Mydll() // 构造函数中做公共类初始化
{
_detImp = new DetImp();
std::cout<<"init Mydll, create _detImp."<<std::endl;
}
Mydll::~Mydll() // 析构函数中做公共类析构
{
if (_detImp)
{
delete _detImp;
_detImp = nullptr;
}
}
int Mydll::init(structC &c) // init中做参数解析适配,根据关系调用对应子类。
{
std::cout<<"Mydll init."<<std::endl;
structA a;
_detImp->m_clsa.init(a);
structB b;
_detImp->m_clsb.init(b);
return 0;
}
int Mydll::run()
{
std::cout<<"Mydll run."<<std::endl;
_detImp->m_clsa.run();
return 0;
}
编译,生成动态库。
3、测试生成的DLL
1、新建一个基于Qt的控制台应用程序,名称为DllTest,与MyDll放在同一级目录下。
2、建好了以后,就可以进行调用验证了。
调用编译好的动态库需要引用两个东西:动态库的头文件和动态库文件。
1)在.pro中添加对Lib的引用
LIBS += -L$$PWD/../build-mydll-Desktop_Qt_5_14_2_MSVC2017_64bit-Release/release/ -lmydll
2)在引用文件中,添加头文件的引用
#include "../mydll/mydll.h"
main.cpp调用完整代码:
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Mydll mydll = Mydll();
structC c;
mydll.init(c);
mydll.run();
return a.exec();
}
输出结果:
上一篇:Qt创建使用动态链接库工程,下一篇:Qt在命名空间中导出类和函数