QLibrary动态加载类

动态链接

动态库的使用有两种方式,一种是显示链接(Explicit Linking),一种是隐示链接(Implicit Linking)。

隐式链接

隐式链接会在链接生成可执行程序时就确立依赖关系,在该程序启动时,操作系统自动会检查它依赖的动态库,并一一加载到该程序的内存空间,程序员就不需要操心什么时候加载动态库了。比如 VC 编译环境,链接时使用动态库对应的 .lib 文件(包含动态库的导出函数声明,但没有实际功能代码),在 .exe 程序运行前系统会检查依赖的 .dll。隐式链接是最为常见的,所有的编译环境默认都是采用隐式链接的方式使用动态库。

显式链接

动态链接库通常都有其导出函数列表, 告知其他可执行程序可以使用它的哪些函数。可执行程序使用这些导出函数有两种方式:一是在运行时使用主动加载动态库的函数,Linux 里比如用 dlopen 函数打开并加载动态库,Windows 里一般用 LoadLibrary 打开并加载动态库,只有当程序代码执行到这些函数时,其参数里的动态库才会被加载,这就是显式链接。显式链接方式是在运行时加载动态库,其程序启动时并不检查这些动态库是否存在。隐式链接是最为常见的,所有的编译环境默认都是采用隐式链接的方式使用动态库。

一种最直观的表现是,隐示链接的程序在程序启动是缺少动态库无法正常启动,隐示链接在程序启动时不依赖动态库

QLibrary

Qt提供QLibrary动态加载库,使用QLibrary可以在程序运行时加载动态链接库。一个QLibrary的实例作用于一个单一的共享库上。QLibrary提供了一种平台无关的方式访问库中的函数,当然内部是封装了上面所说的平台相关的LoadLibrary 、dlopen 等。QLibrary库的典型用法是去解析一个库中的导出符号,并调用该符号表示的C函数。

动态加载C函数

这个在Qt文档里就有例子,这里简单列举一下。

创建了动态库的工程

导出一个c函数,如

extern "C" {
CPPLIB_EXPORT int MyAdd(int a, int b);
}

cpp实现

int MyAdd(int a, int b)
{
    return a+b;
}

需要注意的是使用C++编译器编译的C库需要extern "C",当然C++使用C库也要使用extern "C"

在调用工程中使用QLibrary动态加载

把动态库放到程序生成目录下,QLibrary会自动查找。如果传入的文件名具有绝对路径,那么会首先尝试加载该目录。如果该文件找不到,QLibrary会使用不同的平台特定的文件前缀或后缀再次尝试,比如Unix和Mac平台的"lib"前缀,Unix平台的".so"后缀,Mac平台的".dylib",Windows平台的".dll"。如果文件名不是绝对路径,QLibrary会修改搜索顺序,首先尝试系统特定的前缀和后缀,紧接着是指定的文件路径。

声明函数指针

using addFunc = int (*)(int, int);

调用

    QLibrary lib("CppLib");
    if(!lib.load()) {
        qDebug() << __FUNCTION__ << "song" << "load err";
        return;
    }

    auto func = (addFunc)lib.resolve("MyAdd");
    auto val = func(1, 2);
    qDebug() << __FUNCTION__ << "song" << val;

动态加载类

动态加载类也是通过导出C函数,不过该函数返回的是类的指针对象,通过指针对象使用类的接口

class CppLib
{
public:
    virtual ~CppLib();

    virtual int add(int a, int b) = 0;
};

class CPPLIB_EXPORT TestLib : public CppLib
{
public:
    virtual ~TestLib();

    int add(int a, int b) override;

};

extern "C" {

CPPLIB_EXPORT void *createCppLib();
}

cpp实现:

CppLib::~CppLib()
{

}

TestLib::~TestLib()
{

}

int TestLib::add(int a, int b)
{
    return a+b;
}

void *createCppLib()
{
    return new TestLib;
}

需要注意的是类必须使用继承的形式,函数导出的指针对象void *,基类为抽象类,接口为纯虚函数,继承自基类实现接口。使用方式大体相同,只不过拿到void *后需强转会相应的类对象

定义函数指针

using cppFunc = void *(*)();
    QLibrary lib("CppLib");
    if(!lib.load()) {
        qDebug() << __FUNCTION__ << "song" << "load err";
        return;
    }

    auto func = (cppFunc)lib.resolve("createCppLib");
    TestLib *cppLibObj = static_cast<TestLib *>(func());
    if(cppLibObj) {
        qDebug() << __FUNCTION__ << "song" << cppLibObj->add(2,3);
    }

这里需要包含头文件才能识别到TestLib类,这样就能使用TestLib的接口了,上面调用C函数不需要头文件也是能正常调用的

结语

本篇文章主要介绍QLibrary加载动态库的基本用法,其中加载C++类的用法Qt文档是没有介绍的,这里介绍其基本的用法,其中接口设计得比较简陋,接口可以使用工厂的方式根据不同的类型返回不同的指针对象;返回指针还可以使用智能指针,这样就能够不用管理指针的释放,实际项目中可根据情况进行改进。

demo地址:QtDemo/QLibraryTest at master · a137748099/QtDemo · GitHub

  • 1
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值