QT 5 同时使用Q_DECLARE_METATYPE(pointdata) 和继承 QObjectUserData

在Qt框架中,QObjectUserDataQ_DECLARE_METATYPE() 宏都与Qt的元对象系统有关,但它们的使用方式有一些特别的限制和兼容性问题。

  1. 关于QObjectUserData

    • QObjectUserData 是一个用来存储用户数据的类。在Qt中,每个 QObject 可以存储一个 QObjectUserData 指针数组,每个指针可以指向一个 QObjectUserData 对象。
    • 这个类通常用于在不修改类定义的情况下,为对象附加额外的数据。
  2. 关于Q_DECLARE_METATYPE()

    • Q_DECLARE_METATYPE() 宏允许类型在Qt的元对象系统中使用,如在 QVariant 中。使用这个宏可以让自定义类型通过 QVariant 进行存储和传递。
    • 宏定义需要类型是完全定义的,且具有公共的默认构造函数、复制构造函数和析构函数。

兼容性问题

  • 由于 QObject 本身和继承自 QObject 的任何类不能被复制,这意味着如果你的类继承自 QObject(或间接通过其他类继承),这个类就不能使用 Q_DECLARE_METATYPE()。因为 QVariant 需要能够复制其存储的类型。
  • 如果你的类仅仅继承自 QObjectUserData 并不包含 QObject 的继承,理论上是可以使用 Q_DECLARE_METATYPE() 的,只要你的类满足可以被复制的要求。但是,这种情况比较少见,因为 QObjectUserData 的设计初衷是作为一种与 QObject 相关的数据扩展。

结论

  • 如果你的类继承自 QObject 或包含 QObject 的成员,则不能使用 Q_DECLARE_METATYPE()
  • 如果你的类仅继承自 QObjectUserData 并且符合可以被复制的条件(例如,提供了公共的构造函数和复制构造函数),那么使用 Q_DECLARE_METATYPE() 是可能的。

 

QObject 本身以及任何从 QObject 继承的类都不能被复制。这是由 QObject 的设计决定的,主要出于以下几个原因:

  1. 信号与槽机制QObject 提供了一个强大的信号与槽机制,用于对象之间的通信。每个 QObject 可以发射信号,这些信号可以被同一个对象或其他对象的槽函数接收。如果 QObject 可以被复制,那么信号和槽的连接就可能会被意外复制,从而导致难以追踪的bug。

  2. 对象的唯一性:每个 QObject 可以有一个父对象和多个子对象。这种父子关系管理着对象之间的层次结构和生命周期。复制 QObject 会复杂化这种父子关系,可能导致父对象和子对象在删除或移动时出现问题。

  3. 设计决策:为了保持对象间关系的清晰和简单,Qt设计者选择禁用 QObject 的复制构造函数和赋值运算符,使它们为私有成员。这样,任何试图复制 QObject 或其派生类的操作都会在编译时被阻止。

因此,如果你需要在 Qt 中使用包含对象状态的类并希望通过 QVariant 来传递,你应该设计这些类为非 QObject 派生类,并确保它们可以安全地被复制和销毁。这样,你就可以使用 Q_DECLARE_METATYPE() 宏来注册你的类型。

“复制”指的是通过拷贝构造函数和拷贝赋值运算符来复制一个对象。在 QObject 的上下文中,以下是具体的含义:

  1. 拷贝构造函数:这是一个构造函数,它初始化一个新对象作为一个已存在对象的副本。在 QObject 的情况下,拷贝构造函数是被明确声明为删除的,这意味着你不能使用一个 QObject 或其派生类的实例来直接初始化另一个实例。

  2. 拷贝赋值运算符:这是一个赋值运算符,它允许将一个对象的状态赋值到另一个已存在的对象上。对于 QObject 及其派生类,拷贝赋值运算符同样被声明为删除,这意味着你不能通过赋值将一个 QObject 或其派生类的状态复制到另一个对象。

这种设计是为了防止对象之间的不当信号连接和复杂化的父子关系,确保 QObject 的对象模型保持简洁和一致。这也意味着在使用 Qt 框架时,应该避免设计需要复制 QObject 或其派生类的场景,而是通过指针和引用来管理对象的生命周期和关系。

这个示例程序主要演示了如何在 Qt 应用程序中创建和使用自定义数据类型 `PointData` 和 `Point`。程序通过结合使用 `Q_DECLARE_METATYPE`、`QObjectUserData`,以及 Qt 的属性和变量系统 (`QVariant`),展示了如何有效地管理和使用自定义数据结构。下面是对程序各部分功能的详细解释:

功能和目的

1.PointData` 类的定义与注册
   - `PointData` 是一个简单的类,用于存储和操作二维坐标点(x, y)。
   - 使用 `Q_DECLARE_METATYPE(PointData)` 宏声明这个类,使其可以被 Qt 的元类型系统识别。这是必要的步骤,以便 `PointData` 对象可以安全地存储在 `QVariant` 类型中,并在 Qt 的信号和槽系统中使用。

2. `Point` 类的定义
   - `Point` 类继承自 `QObjectUserData`,允许它作为用户自定义数据被存储在任何 `QObject` 实例中。这为 `Point` 实例的存储提供了便利,尤其是在需要将额外数据附加到 `QObject` 对象上时。

3. 应用程序主函数的实现
   - 元类型注册:通过 `qRegisterMetaType<PointData>()` 确保 `PointData` 类型被 Qt 系统识别,这是使用自定义类型与 `QVariant` 相互操作的前提。
   - 使用 `QVariant` 管理自定义数据:创建 `PointData` 实例,并通过 `QVariant` 系统演示如何将自定义类型封装和解封,从而验证元类型的注册和使用。
   - 使用 `QObjectUserData`创建 `Point` 实例,并将其作为用户数据附加到 `QObject` 对象上,然后从该对象中检索 `Point` 实例。这展示了如何在运行时动态地管理额外的对象信息。

 总体目的

- 演示 Qt 的灵活性和功能:通过使用 `Q_DECLARE_METATYPE` 和 `QObjectUserData`,这个示例展示了 Qt 如何处理自定义类型的封装和传输,以及如何将额外的数据动态地附加到 `QObject` 实例。
- 教育和示范:代码提供了一个实际的例子,说明了如何在 Qt 应用程序中创建、管理和使用自定义数据类型,是学习 Qt 开发的有用资源。

通过这种方式,示例程序不仅展示了 Qt 的核心概念和功能,还提供了一个关于如何扩展 Qt 应用程序功能的实际案例,特别适用于那些需要在应用程序中处理复杂数据结构和进行高级数据管理的场景。

在 Qt 框架中,Q_DECLARE_METATYPE 宏用于注册一个自定义类型,使之可以使用 Qt 的元类型系统。这种注册使得该类型能够在 Qt 的一些高级特性中使用,如信号和槽系统、变量传递等。具体来说,使用 Q_DECLARE_METATYPE 之后,你的类型就可以安全地用于 QVariant,这是 Qt 用于存储能够包含任何类型的通用容器。

功能和用途

当你声明 Q_DECLARE_METATYPE(PointData) 时,你允许 PointData 类型的对象被包装进 QVariant 对象。这样做有几个好处:

  1. 使用信号和槽:使得 PointData 类型可以安全地作为参数在信号和槽之间传递。没有注册的类型不能用于信号和槽系统中,除非它们是内置的 Qt 类型。

  2. 动态属性:允许你将 PointData 对象作为动态属性添加到任何 QObject 派生类的实例中。

  3. 变体操作:可以在需要类型安全和灵活操作的场景下,将 PointData 存储和检索为 QVariant。这在需要将数据存储在通用容器中时非常有用,例如,在模型视图架构中传递自定义数据。

// pointdata.h
#ifndef POINTDATA_H
#define POINTDATA_H#include <QMetaType>class pointdata
{
public:int x;int y;pointdata();pointdata(int x, int y);int getX() const;int getY() const;void setX(int x);void setY(int y);
};Q_DECLARE_METATYPE(pointdata)#endif // POINTDATA_H

#include "pointdata.h"pointdata::pointdata(): x(0), y(0)
{}pointdata::pointdata(int x, int y): x(x), y(y)
{}int pointdata::getX() const
{return x;
}int pointdata::getY() const
{return y;
}void pointdata::setX(int x)
{this->x = x;
}void pointdata::setY(int y)
{this->y = y;
}

在 Qt 框架中,QObjectUserData 类是用于将用户数据附加到 QObject 类的实例上的基类。它提供了一个方便的方式来存储额外的、动态的信息与 QObject 相关联,而无需修改 QObject 的源代码或继承自 QObject

使用场景和目的

QObjectUserData 主要被设计用于在运行时向 QObject 实例添加自定义数据,而不改变其类定义。这对于如插件系统等需要在没有修改现有类的情况下扩展对象功能的场景特别有用。

基本概念

当你继承自 QObjectUserData 并创建自定义类时,你可以利用 Qt 对象系统的功能,将这个自定义类的实例存储为 QObject 的一部分。每个 QObject 可以存储多个 QObjectUserData 对象,每个对象都与一个唯一的整数键相关联。

// point.h
#ifndef POINT_H
#define POINT_H#include <QObjectUserData>
#include <QSharedPointer>
#include "pointdata.h"class Point : public QObjectUserData
{
public:pointdata data;public:Point();Point(int x, int y);int getX() const;int getY() const;void setX(int x);void setY(int y);QSharedPointer<Point> clone() const;
};#endif // POINT_H
#include "point.h"Point::Point() {}Point::Point(int x, int y): data(x, y)
{}int Point::getX() const
{return data.getX();
}int Point::getY() const
{return data.getY();
}void Point::setX(int x)
{data.setX(x);
}void Point::setY(int y)
{data.setY(y);
}QSharedPointer<Point> Point::clone() const
{return QSharedPointer<Point>::create(data.getX(), data.getY());
}

主函数:

  • 注册 pointdata 类型为 Qt 元类型系统的一部分。这使得 pointdata 类型可以被用于 Qt 的信号和槽机制,并能以类型安全的方式存储在 QVariant 中。
  • 创建一个 pointdata 实例并将其包装进 QVariant 对象。这展示了如何使用自定义类型与 QVariant,以及如何从 QVariant 中提取自定义类型的实例。使用 QVariant 是 Qt 中管理泛型数据的常见方式。
  • 使用 Point 类实例作为用户数据存储在 QObject 中。这个示例演示了如何将自定义的用户数据附加到 Qt 对象,并在之后检索使用这些数据。这是利用 QObjectUserData 实现的功能。

#include <QApplication>
#include <QDebug>
#include "Point.h"
#include "PointData.h"
#include "mainwindow.h"int main(int argc, char *argv[])
{QApplication a(argc, argv);// Register PointData as a metatypeqRegisterMetaType<pointdata>("PointData");// Using PointDatapointdata pd(3, 4);QVariant var = QVariant::fromValue(pd);if (var.canConvert<pointdata>()) {pointdata extractedData = var.value<pointdata>();qDebug() << "PointData: x =" << extractedData.getX() << ", y =" << extractedData.getY();}// Using Point with QObjectUserDataQObject obj;Point *point = new Point(5, 6); // Create a Point instanceobj.setUserData(0, point);      // Store the Point instance as user data in QObject// Retrieve and use the Point instance from QObjectPoint *retrievedPoint = static_cast<Point *>(obj.userData(0));if (retrievedPoint) {qDebug() << "Point: x =" << retrievedPoint->data.getX()<< ", y =" << retrievedPoint->data.getY();}MainWindow w;w.show();return a.exec();
}

输出:

07:05:33: Starting E:\WD\untitled85\build\Desktop_Qt_5_15_2_MSVC2019_64bit-Debug\debug\untitled85.exe...
PointData: x = 3 , y = 4
Point: x = 5 , y = 6
07:05:38: E:\WD\untitled85\build\Desktop_Qt_5_15_2_MSVC2019_64bit-Debug\debug\untitled85.exe 退出,退出代码: 0{1 ?} {2?}

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

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

相关文章

【漏洞复现】ServiceNow UI Jelly模板注入(CVE-2024-4879)

声明&#xff1a;本文档或演示材料仅用于教育和教学目的。如果任何个人或组织利用本文档中的信息进行非法活动&#xff0c;将与本文档的作者或发布者无关。 一、漏洞描述 ServiceNow是一家专注于提供企业级云计算服务的企业&#xff0c;其旗舰产品是基于云的服务管理解决方案&…

视觉巡线小车(STM32+OpenMV)——总结

文章目录 目录 文章目录 前言 一、效果展示 二、完整流程 1、STM32CubeMX配置 2、Keil编辑 3、硬件接线 4、参数调试 5、图像处理调试 三、总结 前言 基于前面的系列文章&#xff0c;已基本介绍完了基于STM32OpenMV的视觉巡线小车&#xff0c;本文将以小编自己的小车…

SeaCMS海洋影视管理系统远程代码执行漏洞复现

SeaCMS海洋影视管理系统远程代码执行漏洞复现 Ⅰ、环境搭建Ⅱ、漏洞复现Ⅲ、漏洞分析 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&…

全栈嵌入式C++、STM32、Modbus、FreeRTOS和MQTT协议:工业物联网(IIoT)可视化系统设计思路(附部分代码解析)

项目概述 随着工业4.0时代的到来&#xff0c;工业物联网&#xff08;IIoT&#xff09;在提高生产效率、降低运营成本和实现智能制造方面得到了广泛应用。本项目旨在开发一个全面的工业物联网监控系统&#xff0c;能够实时监测设备的温度、压力、振动和电流等参数&#xff0c;并…

【Python实战因果推断】60_随机实验与统计知识2

目录 An A/B Testing Example An A/B Testing Example 在许多公司中&#xff0c;一种常见的策略是提供廉价甚至免费的产品&#xff0c;这种产品本身可能并不盈利&#xff0c;但其目的是吸引新客户。一旦公司获得了这些客户&#xff0c;就可以向他们推销其他更盈利的产品&#x…

ThinkPHP一对一关联模型的运用(ORM)

一、序言 最近在写ThinkPHP关联模型的时候一些用法总忘&#xff0c;我就想通过写博客的方式复习和整理下一些用法。 具体版本&#xff1a; topthink/framework&#xff1a;6.1.4topthink/think-orm&#xff1a;2.0.61 二、实例应用 1、一对一关联 1.1、我先设计了两张表&#x…

[SWPU2019]Web1

上来看到两个功能&#xff0c;登录和注册&#xff0c;看到登录框直接sqlmap嗦一下 失败 注册admin显示被注册&#xff0c;那就注册一个账密都为aaa 登录进来发现两个功能点 发了一个广告却显示代管理确认&#xff0c;这里肯定没有管理员&#xff0c;所以我们得想办法自己上去a…

铲屎官的必备好物——希喂、352、米家养宠空气净化器分享

对于每一位深爱着家中萌宠的铲屎官而言&#xff0c;无尽的温情往往也伴随着日常生活中的小烦恼。那些不经意间飘散在空气中的毛发&#xff0c;偶尔缠绕在鼻腔或口腔中的细微触感&#xff0c;以及偶尔袭来的不明异味&#xff0c;都是与宠物共度的日子里不可或缺的一部分。幸好随…

聊聊RNNLSTM

RNN 用于解决输入数据为&#xff0c;序列到序列(时间序列)数据&#xff0c;不能在传统的前馈神经网络(FNN)很好应用的问题。时间序列数据是指在不同时间点上收集到的数据&#xff0c;这类数据反映了某一事物、现象等随时间的变化状态或程度&#xff0c;即输入内容的上下文关联…

基于Orangepi全志H616开发嵌入式数据库——SQLite

目录 一、SQLite数据库 1.1 SQLite 的特点&#xff1a; 1.2 SQLite 的使用场景&#xff1a; 1.3 SQLite数据库与传统MySQL数据库的区别&#xff1a; 二、SQLite数据库安装 2.1 SQLite数据库安装方式一&#xff1a; 2.2 SQlite数据库安装方式二&#xff1a; 三、SQLite数…

Nacos适配达梦数据库并制作镜像

背景&#xff1a;因项目需要信创&#xff0c;需将原本的mysql数据库&#xff0c;改成达梦数据库 一、部署达梦数据库 1.1 部署达梦数据库服务 可参考&#xff1a;Docker安装达梦数据库_达梦数据库docker镜像-CSDN博客 1.2 创建nacos数据库 create user SAFE_NACOS identifi…

放大电路总结

补充: 只有直流移动时才有Rbe动态等效电阻 从RsUs看进去,实际上不管接了什么东西都能够看成是一个Ri(输入电阻) Ri Ui/Ii Rb//Rbe Ui/Us Ri/(RiRs) Aus (Uo/Ui)*(Ui/Us) Au *Ri/(RiRs) 当前面是一个电压源的信号 我们就需要输入电阻更大 Ro--->输出电阻--->将…

VSCode+git的gitee仓库搭建

​ 在此之前你已经在gitee创建好了账号&#xff0c;并新建了一个仓库。 1. 安装 Visual Studio Code Visual Studio Code 是编辑 Markdown 和站点配置文件的基础&#xff0c;以下将其简称为 VSCode&#xff0c;你可以在它的 官方网站 下载到它。 如若不理解各个版本之间的区别…

【C++ —— 用一棵红黑树同时封装出map和set】

C —— 用一棵红黑树同时封装出map和set 总览RBTreeMyMapMySet 红黑树源代码红黑树模板参数的控制模板参数中仿函数的增加迭代器模拟1. 迭代器的定义和结构2. 迭代器的操作符重载 set模拟map模拟代码红黑树的代码set的代码map的代码 总览 RBTree enum Colour {RED,BLACK };//…

算法板子:Trie树——存储字符串、查找字符串的出现次数

#include <iostream> using namespace std;const int N 1e6 10;// Trie树是一个集合&#xff0c;可以存储字符串 // son二维数组中&#xff0c;每行代表一个节点&#xff0c;该行的每列都是它的儿子&#xff0c;最多26列代表一个节点最多26个儿子(题目中说了都是小写字…

photoshop学习笔记——选区3

从窗口面板可以打开历史记录面板&#xff0c;历史记录面板保存了所有的操作 可以点击历史记录中某一条&#xff0c;回到当时的操作状态&#xff0c;也可以通过编辑中的 还原、重做、切换到最终状态逐步调整或直接跳到最终状态 回退之后&#xff0c;如果有新的操作&#xff0c;历…

WordPress文章标题定制化前缀插件

引言 在当今互联网的海洋中&#xff0c;吸引读者眼球的第一步往往始于文章标题的设计。对于WordPress博主而言&#xff0c;如何让每篇文章的标题更加个性化和吸引人&#xff0c;成为了一项重要的任务。传统的自定义CSS方法虽然可行&#xff0c;但其繁琐的操作和有限的美学效果…

【高校科研前沿】浙江农林大学童再康教授等人在农林科学顶刊《CATENA》发文:长期覆盖作物可促进多养分循环和地下土壤碳封存

文章简介 论文名称&#xff1a;Long-term cover crops boost multi-nutrient cycling and subsurface soil carbon sequestration by alleviating microbial carbon limitation in a subtropical forest&#xff08;长期覆盖作物通过缓解亚热带森林中微生物的碳限制&#xff0c…

常见的jmeter面试题及答案

1、解释什么是JMeter? JMeter是一款Java开源工具&#xff0c; 用于性能负载测试。它旨在分析和衡量Web应用程序和各种服务的性能和负载功能行为。 2、说明JMeter的工作原理? JMeter就像一群将请求发送到目标服务器的用户-样。它收集来自目标服务器的响应以及其他统计数据&…

每日OJ_牛客_HJ91 走方格的方案数

目录 牛客HJ91 走方格的方案数 解析代码 牛客HJ91 走方格的方案数 走方格的方案数_牛客题霸_牛客网 解析代码 本题为求取路径总数的题目&#xff0c;一般可以通过递归求解&#xff0c;对于复杂的问题&#xff0c;可以通过动态规划求解。此题比较简单&#xff0c;也可以通过递…