Qt元类型系统(QMetaType)详解

Qt元类型系统详解

  • 一、Qt元类型系统(QMetaType)详解
    • 1. 核心功能
    • 2. 注册机制
    • 3. 关键技术点
    • 4. 信号槽支持
    • 5. 流式传输支持
    • 6. 使用场景
    • 7. 注意事项
  • 二、完整示例
    • 1、基本实例
    • 2、基本实例
    • 3、元类型在信号槽中的应用
    • 4、高级用法
  • 三、元对象编译器moc
    • 元对象编译器(Moc)简介
    • Moc的工作原理

一、Qt元类型系统(QMetaType)详解

Qt元类型系统是Qt框架的核心机制之一,为运行时类型信息(RTTI)提供支持,使Qt的信号槽、属性系统、QVariant等机制能够处理自定义数据类型。以下是关键要点:

1. 核心功能

  • 类型注册:为自定义类型分配唯一ID
  • 动态创建:运行时构造/析构对象
  • 类型转换:支持QVariant与自定义类型互转
  • 跨线程通信:确保类型安全的数据传递
  • 类型信息:提供类型名称、大小等元信息

2. 注册机制

声明宏(头文件中):

class CustomType { /*...*/ };
Q_DECLARE_METATYPE(CustomType)  // 声明元类型

运行时注册(cpp文件中):

qRegisterMetaType<CustomType>("CustomType");  // 注册到元对象系统

要使自定义类型能够用于Qt的元类型系统,需要满足以下条件:

  • 必须是值类型(可拷贝)
  • 具有公共的默认构造函数
  • 具有公共的拷贝构造函数
  • 具有公共的析构函数

3. 关键技术点

  • 类型ID获取
    int typeId = qMetaTypeId<CustomType>();  // 获取唯一类型ID
    
  • 动态对象操作
    void* obj = QMetaType::create(typeId);    // 创建实例
    QMetaType::destroy(typeId, obj);          // 销毁实例
    
  • QVariant集成
    CustomType data;
    QVariant var = QVariant::fromValue(data);  // 包装为QVariant
    CustomType copy = var.value<CustomType>(); // 解包数据
    

4. 信号槽支持

注册后可在跨线程信号槽中使用:

// 信号声明
signals:void dataReady(const CustomType&);// 连接前注册(确保线程安全)
qRegisterMetaType<CustomType>();
connect(sender, &Sender::dataReady, receiver, &Receiver::handleData);

5. 流式传输支持

如需支持QDataStream序列化:

// 注册流操作符
qRegisterMetaTypeStreamOperators<CustomType>("CustomType");// 实现操作符重载
QDataStream& operator<<(QDataStream& out, const CustomType& obj);
QDataStream& operator>>(QDataStream& in, CustomType& obj);

6. 使用场景

  1. QVariant数据容器:存储任意类型数据
    QVariantList list;
    list << QVariant::fromValue(CustomType());
    
  2. 动态属性系统
    QObject obj;
    obj.setProperty("customProp", QVariant::fromValue(CustomType()));
    
  3. 跨线程通信:保证自定义类型在信号槽中的类型安全

7. 注意事项

类型注册的必要性
自定义类型必须通过qRegisterMetaType()Q_DECLARE_METATYPE()注册,否则无法用于信号槽跨线程通信或QVariant存储。基本类型(如intQString)已由Qt内置注册。

线程安全与信号槽
跨线程传递自定义类型时,必须确保类型已注册且可构造/复制/销毁。未注册类型会导致运行时警告:“QObject::connect: Cannot queue arguments of type ‘MyClass’”。

QVariant的限制
使用QVariant::fromValue()QVariant::value<T>()时,类型必须满足:

  • 默认构造函数
  • 拷贝构造函数
  • 公开的析构函数
  • 使用Q_DECLARE_METATYPE宏声明

类型名称冲突
避免不同类型使用相同名称注册,否则可能导致运行时行为异常。可通过QMetaType::type("MyClass")检查是否已注册。

动态多态类型处理
涉及继承的类需额外处理:

// 基类注册
Q_DECLARE_METATYPE(MyBaseClass*)
// 派生类注册
qRegisterMetaType<MyDerivedClass*>("MyDerivedClass*");

移动语义支持
Qt 5及更高版本支持移动语义,但需确保类型实现移动构造函数和移动赋值运算符。对于资源管理类尤为重要。

模板类型处理
模板类需显式实例化注册:

typedef QMap<QString, MyClass> MyClassMap;
Q_DECLARE_METATYPE(MyClassMap)

二、完整示例

1、基本实例

// 自定义类型
struct Point3D {double x, y, z;Point3D(double a=0, double b=0, double c=0) : x(a), y(b), z(c) {}
};
Q_DECLARE_METATYPE(Point3D)// 主程序
int main() {qRegisterMetaType<Point3D>("Point3D");// QVariant使用Point3D p(1.0, 2.0, 3.0);QVariant var = QVariant::fromValue(p);Point3D p2 = var.value<Point3D>();// 动态创建int id = qMetaTypeId<Point3D>();void* mem = QMetaType::create(id);QMetaType::destroy(id, mem);return 0;
}

2、基本实例

Test.h

#ifndef TEST_H
#define TEST_H#include <QMetaType>
#include <QString>class Test {
public:Test (int numerator = 0, int denominator = 1): m_numerator(numerator), m_denominator(denominator) {}// 编译器生成的默认拷贝构造函数和析构函数满足要求QString toString() const {return QString("%1/%2").arg(m_numerator).arg(m_denominator);}double toDouble() const {returnstatic_cast<double>(m_numerator) / m_denominator;}int numerator() const { return m_numerator; }int denominator() const { return m_denominator; }private:int m_numerator;int m_denominator;
};Q_DECLARE_METATYPE(Test )  // 声明Fraction为元类型#endif // Test 

main.cpp

#include <QCoreApplication>
#include <QVariant>
#include <QDebug>
#include "fraction.h"int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);// 注册元类型(用于信号槽连接)qRegisterMetaType<Test>();// 创建Fraction对象Fraction f1(3, 4);Fraction f2(1, 2);// 使用QVariant存储自定义类型QVariant v1 = QVariant::fromValue(f1);QVariant v2;v2.setValue(f2);// 从QVariant中获取值if (v1.canConvert<Fraction>()) {Fraction f = v1.value<Fraction>();qDebug() << "v1 contains:" << f.toString() << "=" << f.toDouble();}if (v2.canConvert<Fraction>()) {Fraction f = v2.value<Fraction>();qDebug() << "v2 contains:" << f.toString() << "=" << f.toDouble();}// 检查类型信息qDebug() << "Type name:" << QMetaType::typeName(qMetaTypeId<Fraction>());qDebug() << "Type size:" << QMetaType::sizeOf(qMetaTypeId<Fraction>());// 动态创建Fraction实例void *ptr = QMetaType::create(qMetaTypeId<Fraction>());if (ptr) {Fraction *f = static_cast<Fraction*>(ptr);qDebug() << "Dynamically created:" << f->toString();QMetaType::destroy(qMetaTypeId<Fraction>(), ptr);}return a.exec();
}

3、元类型在信号槽中的应用

元类型系统使得自定义类型可以用于信号槽连接:

// 在头文件中
signals:void fractionAdded(Fraction f);// 连接信号槽
QObject::connect(sender, &Sender::fractionAdded, receiver, &Receiver::handleFraction);// 需要在使用前调用
qRegisterMetaType<Fraction>("Fraction");

4、高级用法

流操作支持,要使自定义类型支持QDataStream的序列化,需要重载操作符:

QDataStream &operator<<(QDataStream &out, const Fraction &f) {out << f.numerator() << f.denominator();return out;
}QDataStream &operator>>(QDataStream &in, Fraction &f) {int num, den;in >> num >> den;f = Fraction(num, den);return in;
}

类型转换,可以注册自定义类型转换函数:

QMetaType::registerConverter<Fraction, QString>(&Fraction::toString);

注册后,可以直接将Fraction转换为QString:

Fraction f(1, 2);
QString s = QVariant::fromValue(f).toString();

提到元类型就不得不提MOC编译器了。

三、元对象编译器moc

元对象编译器(Moc)简介

元对象编译器(Meta-Object Compiler,简称Moc)是Qt框架的核心工具之一,用于处理Qt的信号与槽机制、运行时类型信息(RTTI)、属性系统等元对象系统功能。Moc在编译前对C++头文件进行预处理,生成额外的元对象代码,使Qt的元编程特性得以实现。

Moc的工作原理

Moc解析包含Q_OBJECT宏的C++头文件,识别信号、槽、属性等标记,并生成对应的元对象代码(通常为moc_*.cpp文件)。生成的代码会被编译并链接到最终程序中,为Qt的动态特性(如信号与槽连接)提供运行时支持。

在这里插入图片描述

源文件qconsole.h

#ifndef QONSOLE_H
#define QONSOLE_H#include <QMainWindow>
#include <QProcess>
#include <QTextEdit>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>class Qonsole : public QMainWindow {Q_OBJECTpublic:Qonsole(QWidget *parent = nullptr) : QMainWindow(parent) {setupUI();setupProcess();}~Qonsole() {if (m_process->state() == QProcess::Running) {m_process->terminate();m_process->waitForFinished();}}private slots:void onReadyRead();void onReturnPressed();void onProcessStarted();void onProcessError(QProcess::ProcessError error);private:void setupUI();void setupProcess();void writeToConsole(const QString &text, const QColor &color = Qt::black);QProcess *m_process;QTextEdit *m_console;QLineEdit *m_commandInput;
};#endif // QONSOLE_H

编译后的moc_qonsole.cpp文件

/****************************************************************************
** Meta object code from reading C++ file 'qonsole.h'
**
** Created by: The Qt Meta Object Compiler version 69 (Qt 6.9.0)
**
** WARNING! All changes made in this file will be lost!
*****************************************************************************/#include "../../../qonsole.h"
#include <QtGui/qtextcursor.h>
#include <QtCore/qmetatype.h>#include <QtCore/qtmochelpers.h>#include <memory>#include <QtCore/qxptype_traits.h>
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'qonsole.h' doesn't include <QObject>."
#elif Q_MOC_OUTPUT_REVISION != 69
#error "This file was generated using the moc from 6.9.0. It"
#error "cannot be used with the include files from this version of Qt."
#error "(The moc has changed too much.)"
#endif#ifndef Q_CONSTINIT
#define Q_CONSTINIT
#endifQT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
QT_WARNING_DISABLE_GCC("-Wuseless-cast")
namespace {
struct qt_meta_tag_ZN7QonsoleE_t {};
} // unnamed namespacetemplate <> constexpr inline auto Qonsole::qt_create_metaobjectdata<qt_meta_tag_ZN7QonsoleE_t>()
{namespace QMC = QtMocConstants;QtMocHelpers::StringRefStorage qt_stringData {"Qonsole","onReadyRead","","onReturnPressed","onProcessStarted","onProcessError","QProcess::ProcessError","error"};QtMocHelpers::UintData qt_methods {// Slot 'onReadyRead'QtMocHelpers::SlotData<void()>(1, 2, QMC::AccessPrivate, QMetaType::Void),// Slot 'onReturnPressed'QtMocHelpers::SlotData<void()>(3, 2, QMC::AccessPrivate, QMetaType::Void),// Slot 'onProcessStarted'QtMocHelpers::SlotData<void()>(4, 2, QMC::AccessPrivate, QMetaType::Void),// Slot 'onProcessError'QtMocHelpers::SlotData<void(QProcess::ProcessError)>(5, 2, QMC::AccessPrivate, QMetaType::Void, {{{ 0x80000000 | 6, 7 },}}),};QtMocHelpers::UintData qt_properties {};QtMocHelpers::UintData qt_enums {};return QtMocHelpers::metaObjectData<Qonsole, qt_meta_tag_ZN7QonsoleE_t>(QMC::MetaObjectFlag{}, qt_stringData,qt_methods, qt_properties, qt_enums);
}
Q_CONSTINIT const QMetaObject Qonsole::staticMetaObject = { {QMetaObject::SuperData::link<QMainWindow::staticMetaObject>(),qt_staticMetaObjectStaticContent<qt_meta_tag_ZN7QonsoleE_t>.stringdata,qt_staticMetaObjectStaticContent<qt_meta_tag_ZN7QonsoleE_t>.data,qt_static_metacall,nullptr,qt_staticMetaObjectRelocatingContent<qt_meta_tag_ZN7QonsoleE_t>.metaTypes,nullptr
} };void Qonsole::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{auto *_t = static_cast<Qonsole *>(_o);if (_c == QMetaObject::InvokeMetaMethod) {switch (_id) {case 0: _t->onReadyRead(); break;case 1: _t->onReturnPressed(); break;case 2: _t->onProcessStarted(); break;case 3: _t->onProcessError((*reinterpret_cast< std::add_pointer_t<QProcess::ProcessError>>(_a[1]))); break;default: ;}}
}const QMetaObject *Qonsole::metaObject() const
{return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}void *Qonsole::qt_metacast(const char *_clname)
{if (!_clname) return nullptr;if (!strcmp(_clname, qt_staticMetaObjectStaticContent<qt_meta_tag_ZN7QonsoleE_t>.strings))return static_cast<void*>(this);return QMainWindow::qt_metacast(_clname);
}int Qonsole::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{_id = QMainWindow::qt_metacall(_c, _id, _a);if (_id < 0)return _id;if (_c == QMetaObject::InvokeMetaMethod) {if (_id < 4)qt_static_metacall(this, _c, _id, _a);_id -= 4;}if (_c == QMetaObject::RegisterMethodArgumentMetaType) {if (_id < 4)*reinterpret_cast<QMetaType *>(_a[0]) = QMetaType();_id -= 4;}return _id;
}
QT_WARNING_POP

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/93250.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/93250.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《C++继承详解:从入门到理解公有、私有与保护继承》

《C继承详解&#xff1a;从入门到理解公有、私有与保护继承》 文章目录《C继承详解&#xff1a;从入门到理解公有、私有与保护继承》一、继承的概念及定义1.1 继承的概念1.2 继承定义1.2.1 定义格式1.2.2 继承基类成员访问方式的变化1.3 继承类模版二、基类和派生类间的转换三、…

佳能iR-ADV C5560复印机如何扫描文件到电脑

打印机与电脑连接首先&#xff0c;确保佳能iR-ADV C5560复印机通过USB或Wi-Fi等网络连接的方式成功连接到电脑。这可以通过USB线缆或Wi-Fi等网络来实现。连接完成后&#xff0c;便可利用打印机内置的扫描功能&#xff0c;轻松将文件扫描并传输至电脑中。【扫描操作步骤】接下来…

腾讯AI IDE

1.官网说明&#xff1a;打开腾讯AI IDE官网。2.安装说明&#xff1a;安装成功后的界面。3.登录 说明&#xff1a;通过邮箱和密码登录。4.成功说明&#xff1a;成功登录如下界面。5.简单一问说明&#xff1a;理解能力感觉不错。拥有Claude-3.7-Sonnet​​&#xff0c;​​Claude…

【LeetCode 热题 100】(一)哈希

1. 两数之和 class Solution {public int[] twoSum(int[] nums, int target) {int length nums.length;// 1.声明一个hashmap {nums[i], i}HashMap<Integer, Integer> map new HashMap<>();for (int i 0; i < length; i) {int second target - nums[i];if(m…

PMOS快速关断电路、PMOS加速关断电路

[电源系列]二、低成本MOS快速关断电路原理分析 MOS的减速加速电路设计 分享一个微碧在网上看到的电路情况 加速电路1 PMOS关断时间较长。 当用100kHz的频率驱动PMOS时&#xff0c;PMOS G极的电压信号并不是一个脉冲波&#xff0c;PMOS一直处于线性放大的状态&#xff0c;并且…

Docker笔记(基本命令、挂载本地gpu、Dockerfile文件配置、数据挂载、docker换源)

Docker 主要用于环境搭建以及服务部署 基本命令 1.查看镜像 docker images 2.查看容器 docker ps # 查看容器仅仅为查看运行状态的容器 docker ps -a # 查看所有状态的容器3.退出容器 exit4.删除镜像、容器 docker rm 镜像ID docker rm 容器ID docker rm -f 容器ID # 强制删除…

算法竞赛阶段二-数据结构(37)数据结构循环链表模拟实现

之前单链表中&#xff0c;数组全初始化为0&#xff0c;末尾最后一个next 存的就是0&#xff0c;指向的就是头节点循环链表的基本概念循环链表是一种特殊的链表&#xff0c;其尾节点的指针域指向头节点&#xff0c;形成一个闭环。与普通单链表相比&#xff0c;循环链表的遍历需要…

20250727让飞凌OK3576-C开发板在Rockchip的原厂Android14下通过耳机播音

20250727让飞凌OK3576-C开发板在Rockchip的原厂Android14下通过耳机播音 2025/7/27 23:28缘起&#xff1a;很容易知道 飞凌OK3576-C开发板 使用的声卡芯片是 NAU88C22YG 新唐科技(NUVOTON) NAU8822LYG NAU88C22YG 新唐立体声音频编解码芯片原理图&#xff1a;OK3576-C V1.2_202…

正向代理和反向代理的理解

**正向代理&#xff08;Forward Proxy&#xff09;和反向代理&#xff08;Reverse Proxy&#xff09;**是两种不同类型的代理服务器&#xff0c;它们在数据传输过程中扮演的角色、使用场景以及工作方式都有所不同。 正向代理&#xff08;Forward Proxy&#xff09; 定义与作用&…

Java 后端 Cookie Session Token会话跟踪技术

概述 会话从字面理解就是"两方交流"&#xff0c;那问题就来了&#xff0c;HTTP&#xff08;超文本传输协议&#xff09;里面的"传输"不就包含了"两方交流"的意思吗&#xff1f;为什么要多此一举提出会话技术呢&#xff1f; 谈到这个&#xff0c;…

智谱AI GLM大模型 GLM-4-Plus的快速使用 ChatOpenAI类来调用GLM-4模型

智谱AIGLM-4&#xff0c;2024年1月16日发布的第四代基座大模型&#xff0c;其整体性能相较前代提升近60%&#xff0c;多维度指标逼近OpenAI的GPT-4水平。该模型支持128K上下文窗口&#xff08;约300页文本处理能力&#xff09;&#xff0c;在长文本信息处理中实现100%精度召回。…

AsyncLocal浅复制的问题解决方案

针对C#中AsyncLocal<T>浅复制问题&#xff0c;以下是几种主要的解决方案&#xff1a; 1. 使用不可变对象&#xff08;推荐&#xff09; 将存储在AsyncLocal<T>中的对象设计为不可变的&#xff0c;避免修改共享状态&#xff1a; public class ImmutableUserContext …

IIS发布.NET9 API 常见报错汇总

记录工作过程中发现的IIS常见错误。 1. HTTP Error 500.19 - Internal Server Error .NET 9 API --》vs打包方式如下&#xff1a; 发布到IIS后报错HTTP Error 500.19 - Internal Server Error。 解决方案&#xff1a; 下载ASP.NET Core Hosting Bundle&#xff08;ASP.NET Co…

Google Chrome V8< 13.7.120 沙箱绕过漏洞

【严重】Google Chrome V8< 13.7.120 沙箱绕过漏洞 漏洞描述 V8 是 Google 开发的一款开源高性能 JavaScript 和 WebAssembly 引擎&#xff0c;广泛用于 Chrome 浏览器和 Node.js 等项目中。 受影响版本中&#xff0c;JsonParser::MakeString 函数在处理长度为 1 的转义字…

基于Spring Boot和Vue电脑维修平台整合系统的设计与实现

用户&#xff1a;注册&#xff0c;登录&#xff0c;在线报修&#xff0c;维修接单&#xff0c;维修报告&#xff0c;维修评价&#xff0c;个人资料维修工&#xff1a;登录&#xff0c;在线报修&#xff0c;维修接单&#xff0c;维修报告&#xff0c;维修评价&#xff0c;通知公…

InsightFace(RetinaFace + ArcFace)人脸识别项目(预训练模型,鲁棒性很好)

背景介绍 这是一个 简单的人脸识别项目&#xff0c;用 FastApi 在本地实现&#xff0c;使用预训练模型&#xff0c;直接可用。 新方案比之前的FaceNet强太多了&#xff0c;甚至不用数据增强等操作&#xff0c;就可以识别戴眼镜、不戴眼镜、歪着的人脸等。 充分证明了选型的重要…

App Inventor 2 使用 MaterialIcons 图标字体,快捷展示专业图标

平时布局的话&#xff0c;如果要使用图标&#xff0c;一般需要去找 png 图片&#xff0c;且透明背景的。如果需要根据不同常见图标进行变色的话&#xff0c;就需要准备多张不同样式的图标&#xff0c;还要考虑图片的分辨率等等因素&#xff0c;非常的麻烦。 这时&#xff0c;如…

C语言——关于指针(逐渐清晰版)

为了更好地理解本篇文章的知识内容&#xff0c;读者可以将以下文章作为补充知识进行阅读 &#xff1a; C语言————原码 补码 反码 &#xff08;超绝详细解释&#xff09;-CSDN博客 C语言————二、八、十、十六进制的相互转换-CSDN博客 C语言————斐波那契数列的理解…

SVG 在线编辑器

SVG 在线编辑器 引言 随着互联网技术的发展&#xff0c;矢量图形在网页设计和数据可视化中扮演着越来越重要的角色。SVG&#xff08;可缩放矢量图形&#xff09;因其文件小、无限缩放不模糊的特性&#xff0c;成为了网页设计中常用的图形格式。SVG 在线编辑器的出现&#xff0c…

libpostproc 已经从 ffmpeg 中移除,导致编译 ffmpeg 时没有 libpostproc

今天编译 ffmpeg 时突然发现 libpostproc 不见了&#xff0c;-enable-postproc 也变成了非法的选项。用搜索引擎搜索相关信息找不到一点&#xff0c;于是去 github 看。 从提交记录可以看到 libpostproc 已经被移除了 链接 主线中已经看不到了 libpostproc 这个目录了