当前位置: 首页 > news >正文

Qt Creator:避免QRunnable和QObject多重继承

目录

        1.前言

        2.简单介绍QRunnable

        3.如何使用QRunnable

        4.为什么要继承QObject

        5.如何避免多重继承QRunnable和QObject

        6.QMetaObject::invokeMethod进阶版


前言

        Qt中主要存在三种使用线程的方式,分别是QThread,QRunnable和moveToThread。其中QThread的使用场景主要是简单的独立任务,或不需频繁交互的耗时操作,QRunnable的使用场景主要是复杂的、需长时间运行并与主线程频繁交互的后台服务,而moveToThread的使用场景则是大量独立、无状态的短期任务。本篇文章主要是讲解QRunnable,而由于大多数博客在使用的时候都是采用多重继承的方式继承了QRunnable和QObject,这种方式并没有保持QRunnable轻量的特性。故此本篇文章将解决这个问题,使用QMetaObject单独继承QRunnable。


简单介绍QRunnable

        在讲解解决方案之前,先了解一下QRunnable。

                1.QRunnable的核心优势是极致的轻量,内存与性能开销小,代码复杂度低

                2.QRunnable的缺点是不属于QObject的子类,导致不能使用信号槽

                3.QRunnable的使用场景是大量、独立、生命周期短的​​计算密集型任务


如何使用QRunnable

        对于如何使用QRunnable,以下是示例代码(不保证能运行):

//----------第一步,自定义类型继承自QRunnable-----------
#include <QRunnable>
class Task : public QRunnable
{
public:explicit Task(){setAutoDelete(true);    // 自动删除对象}void run(){qDebug() << "WildPointer"; }
};//----------第而步,创建对象使用线程池启动线程-----------
int main(int argc, char *argv[])
{Task *test = new Task();QThreadPool::globalInstance()->start(task);    // 使用全局线程池激活线程
}


为什么要继承QObject

        大多数Qt开发者习惯使用信号槽的方式去通知其他对象事件处理的结束,当然我们也可以使用全局对象,共享内存,锁等方式实现,但是在Qt下能使用信号槽实现相同的功能,代码简单容易实现何乐而不为呢?但是由于QRunnable并不是QObject的子类,所以单独继承QRunnable不能使用信号槽,于是大多数情况便采用多重继承的方式同时继承QRunnable和QObject。这种实现方式并不能保持QRunnable轻量级减少开销​,而且还​​必须将 QObject写在继承列表的第一个位置​才能正确正确的生成元对象代码,否则会在编译时报错

#include <QObject>
#include <QRunnable>// 同时继承QRunnable和QObject
class Task : public QObject, public QRunnable
{Q_OBJECT
public:explicit Task(QObject *parent = nullptr);void run() override;
};

如何避免多重继承QRunnable和QObject

        为了避免同时继承QRunnable和QObjet,将采用QMetaObject::invokeMethod。该函数的原理如图所示:

图1.QMetaObject::invokeMethod调用原理

        在使用QMetaObject::invokeMethod调用函数之前,需要将被调用的函数使用Q_INVOKABLE宏来声明。Q_INVOKABLE关键字是一个宏,用于标记一个普通的C++成员函数,使其能够被Qt的元对象系统识别,从而能被QMetaObject::invokeMethod进行动态调用。具体的使用Q_INVOKABLE的代码如下:

// 使用Q_INVOKABLE声明函数
Q_INVOKABLE void Function(QString str);// 函数实现
Q_INVOKABLE void Function(QString str){qDebug() << str;
}

        在我们使用Q_INVOKABLE声明被调用的函数以后,我们需要在继承自QRunnable类型中使用QMetaObject::invokeMethod调用。具体步骤如下:
        1.
继承QRunnable类型并且在构造函数中添加父类作为形参

#include <QObject>
#include <QRunnable>class WildPointer : public QRunnable
{
private:QObject m_Obj = nullptr;	// 调用对象
public:~ WildPointer();void run() override;// 大多数Qt中的类型都继承自QObject,所以此处使用QObject作示例explicit WildPointer(QObject *parent);
};// 构造函数的实现
WildPointer::WildPointer(QObject* parent), m_Obj(parent)
{setAutoDelete(true);
}

        2.在需要调用外部函数的时候,使用QMetaObject::invokeMethod

void WildPointer::Function() {// 调用被 Q_INVOKABLE 标记的函数QMetaObject::invokeMethod(m_Obj, "Function", Qt::AutoConnection, Q_ARG(QString, QString("WildPointer")));
}

QMetaObject::invokeMethod进阶版

        当我们调用继承自QRunnable类型的对象变多的时候,类型可能也会不同。此时我们需要针对不同的类型调用对应类型中使用Q_INVOKABLE宏声明的函数,这个时候我们单独写一条QMetaObject::invokeMethod函数调用会导致很难对很多类型都做适配,于是我们可以对传入的类对象进行判断,执行不同类中的函数,具体修改如下:
        1.
在继承自QRunnable类的类型中定义回调函数指针

#include <QObject>
#include <QRunnable>
#include <functional>class WildPointer : public QRunnable
{
private:QObject m_Obj = nullptr;	// 调用对象std::function<void(bool, QString)> m_OnResult; // 回调函数
public:~ WildPointer();void run() override;// 大多数Qt中的类型都继承自QObject,所以此处使用QObject作示例explicit WildPointer(QObject *parent);
};// 构造函数的实现
WildPointer::WildPointer(QObject* parent), m_Obj(parent)
{setAutoDelete(true);
}

        2.定义设置回调函数的接口

// 设置结果回调
void WildPointer::setOnResultCallback(std::function<void(bool, QString)> callback) {m_OnResult = std::move(callback);
}

        3.通过QMetaObject::invokeMethod调用回调函数

void WildPointer::ReturnResultMessage(bool state, QString message) {if (m_fpObj) {// 将回调切回创建者线程执行,确保线程安全QMetaObject::invokeMethod(m_Obj, [callback = m_OnResult, result = state, msg = std::move(message)](){if (callback) {callback(result, msg);}}, Qt::AutoConnection);}
}

PS:通过进阶版的示例,我们可以实现一个供任意对象调用的模块,并且动态的调用每一个对象中的函数,实现所谓的反射

http://www.dtcms.com/a/529247.html

相关文章:

  • 抽奖机网站怎么做wordpress小工具是哪个文件夹
  • 简述网站推广的方法做一个app需要多少成本
  • JsonRpc框架介绍和环境搭建
  • 网站右下角悬浮窗口js代码 兼容各浏览器如何看还在建设的网站
  • qq浏览器收录网站提交入口wordpress 博客 视频
  • 关于宽带网络下公网地址的理解
  • 【面经】2025年软件测试面试题,精选100 道(附答案)
  • 网站风格优势网页设计哪里好
  • 案例研究_CVE-2018-1000517
  • 内存补丁技术:破解与防护的终极对决
  • 20251019状压DP总结
  • 西部数码网站管理助手v3.0产品设计ppt案例
  • 【2073】三角形面积
  • Datawhale秋训营-“大运河杯”数据开发应用创新大赛
  • 7(内存管理)(上)(了解)
  • 成都个人建网站ps软件下载电脑版免费怎么下载
  • 解析常见的限流算法
  • 潼南区做网站的公司中国医院建设协会网站
  • 夸克 × 大模型:从“搜索工具”到“智能体”的演化逻辑
  • 网站正在建设中色综合网页程序开发工具
  • 个人怎么开网上超市福州百度seo
  • 笔试强训(八)
  • 山西品牌网站建设成品网站源码68w68游戏
  • Linux内核进程管理子系统有什么第六十八回 —— 进程主结构详解(64)
  • 做网站需要商标注册吗阿里巴巴怎么做企业网站
  • 做视频网站需要哪些技术指标wordpress可以放视频吗
  • 动态库的使用-openssl
  • Maven 项目和 Maven Web 项目的异同点
  • Maven整理
  • 关于OpenAI CLIP的综合技术报告:架构、对比预训练与多模态影响