20、元对象系统---------QT基础

元对象系统

Qt 的元对象系统(Meta-Object System)是 Qt 框架的核心特性之一,它支持信号与槽机制、动态属性、反射等功能。元对象系统使得 Qt 能够实现许多高级功能,如动态类型信息、对象树管理、事件处理等。

使用元对象系统需要注意:

●QObject 类是所有使用元对象系统的类的基类。

●必须在一个类的开头部分插入宏 Q_OBJECT,这样这个类才可以使用元对象系统的特性。

信号与槽机制

信号与槽机制是 Qt 的核心特性之一,用于对象之间的通信。信号是由对象发出的事件,槽是处理这些事件的函数。这一节仅作演示说明元对象系统支持信号槽机制,具体的信号和槽知识后续介绍。

定义信号与槽

在类中使用 signals 和 slots 关键字定义信号和槽:

切记元对象系统的类必须继承或间接继承自QObject,类的声明里包含Q_OBJECT

cpp 复制代码
#include <QObject>
#include <QDebug>

class MyObject : public QObject
{
    Q_OBJECT

public:
    MyObject(QObject *parent = nullptr) : QObject(parent) {}
//定义信号
signals:
    void mySignal(int value);
//定义槽函数
public slots:
    void mySlot(int value) {
        qDebug() << "Slot called with value:" << value;
    }
};
连接信号与槽

使用QObject::connect 函数将信号连接到槽:

cpp 复制代码
int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    MyObject obj;
    //连接信号和槽
    QObject::connect(&obj, &MyObject::mySignal, &obj, &MyObject::mySlot);
    //发送信号
    emit obj.mySignal(42); // 发出信号

    return app.exec();
}

上述代码输出

cpp 复制代码
Slot called with value: 42

属性系统

对于元对象类,我们可以通过Qt的属性系统允许在运行时查询和操作对象的属性,这些操作在开发中用的不多,可以了解一下即可。

定义属性

使用 Q_PROPERTY 宏定义属性:

我们在MyObject类的声明中添加属性定义

cpp 复制代码
 Q_PROPERTY(int myProperty READ myProperty WRITE setMyProperty NOTIFY myPropertyChanged)

上面定义了几个属性:

  1. 定义了myProperty属性,为int类型成员变量
  2. 定义了myPropery属性 ,为READ函数类型
  3. 定义了setPropery属性, 为WRITE函数类型
  4. 定义了myPropertyChanged属性, 为NOTIFY类型,也就是信号类型
定义成员函数

接下来定义这些函数和成员, 函数名字需要和Q_PROPERTY中定义的一样

cpp 复制代码
class MyObject : public QObject
{
    Q_OBJECT
    Q_PROPERTY(int myProperty READ myProperty WRITE setMyProperty NOTIFY myPropertyChanged)

public:
    MyObject(QObject *parent = nullptr) : QObject(parent), m_myProperty(0) {}
    //读函数,返回成员m_myPropery的值
    int myProperty() const { return m_myProperty; }
    //写函数, 设置m_myProperty成员
    void setMyProperty(int value) {
        if (m_myProperty != value) {
            m_myProperty = value;
            emit myPropertyChanged(m_myProperty);
        }
    }

signals:
    //信号
    void myPropertyChanged(int value);

private:
    //成员变量
    int m_myProperty;
};
属性访问和设置

可在主函数中设置属性值

cpp 复制代码
MyObject obj;
//设置属性值为42
obj.setMyProperty(42);

因为我们的MyObject继承了QObject,且类的内部声明了Q_OBJECT, 所以可以将MyObject对象转化为元对象

cpp 复制代码
//返回元对象
const QMetaObject *metaObject = obj.metaObject();

元对象可以根据属性名字返回属性所在的索引,因为我们之前定义的属性名为myProperty, 这里获取属性名myProperty所在的索引

cpp 复制代码
//根据属性名字返回属性所在的索引
int propertyIndex = metaObject->indexOfProperty("myProperty");

我们可以根据属性索引获取具体的属性对象, 在打印这个属性对象的名字和数值

cpp 复制代码
QMetaProperty metaProperty = metaObject->property(propertyIndex);
//分别打印属性
qDebug() << "Property name:" << metaProperty.name();
qDebug() << "Property value:" << metaProperty.read(&obj).toInt();
qDebug() << "Property value:" << obj.property(metaProperty.name()).toInt();

上面的代码输出

cpp 复制代码
Property name: myProperty
Property value: 42
Property value: 42
动态属性

Qt 允许在运行时动态添加和获取属性:

cpp 复制代码
obj.setProperty("dy_pro",1024);
qDebug() << "dynamic_property is " << obj.property("dy_pro").toInt();

上面的代码输出

cpp 复制代码
dynamic_property is  1024

对象树管理

Qt 的对象树管理是通过 QObject 类的父子关系实现的。每个 QObject 对象可以有一个父对象和多个子对象,这种层次结构被称为对象树。对象树管理提供了一种方便的方式来管理对象的生命周期和层次结构。

核心概念
  1. 父对象和子对象
    • 每个 QObject 对象可以有一个父对象和多个子对象。父对象负责管理子对象的生命周期,当父对象被销毁时,所有子对象也会被销毁。
  1. 树状结构
    • QObject 对象的父子关系形成了一棵树。根对象没有父对象,所有其他对象都有一个唯一的父对象。
  1. 自动销毁
    • 当一个对象被销毁时,它的所有子对象也会被自动销毁。这种机制有助于避免内存泄漏。

我们可以构造如下图的对象树

代码演示

我们可以定义一个类TreeObj表示对象树的类

cpp 复制代码
class TreeObj: public QObject
{
    Q_OBJECT
public:
    TreeObj(const QString& name, QObject* parent = nullptr)
        : QObject(parent){
        setObjectName(name);
    }

    ~TreeObj(){
        qDebug() << QString("call %1 destruction ").arg(objectName());
    }
};
  1. setObjectName 为QObject的函数,主要用来设置对象名称。
  2. TreeObj构造函数接受一个字符串和父节点,字符串表示对象名称,父节点用来构造基类QObject。

我们写一个函数,构造一个父节点Parent,在父节点下面构造Child1和Child2,然后又在Child1下构造Grandchild1和Grandchild2.

cpp 复制代码
void object_tree(){
    auto parent = new TreeObj("Parent");
    auto child1 = new TreeObj("Child1",parent);
    auto child2 = new TreeObj("Child2",parent);
    auto grand_child1 = new TreeObj("Grandchild1",child1);
    auto grand_child2 = new TreeObj("Grandchild2",child1);
    qDebug() << QString("parent has %1 children  ")
                .arg(parent->children().count());
    qDebug() << QString("child1 has %1 children  ")
                .arg(child1->children().count());
}

上面的函数输出

cpp 复制代码
"parent has 2 children  "
"child1 has 2 children  "

因为三个对象都是new出来的,按道理应该手动delete回收,但是有元对象系统,我们只需要delete最上层的父节点,他的所有子节点,以及子节点的孩子都会被回收

cpp 复制代码
delete parent;

执行删除后,程序输出如下

cpp 复制代码
"call Parent destruction "
"call Child1 destruction "
"call Grandchild1 destruction "
"call Grandchild2 destruction "
"call Child2 destruction "

可以看到所有子节点自动被回收了,因为对象树机制自动管理子节点的生命周期会随着父节点的结束而结束。

类型转换

qobject_cast 是 Qt 框架提供的一个类型转换函数,专门用于在 QObject 类及其派生类之间进行安全的类型转换。它类似于 C++ 标准库中的 dynamic_cast,但仅适用于 QObject 类型。

qobject_cast 使用 Qt 的元对象系统(Meta-Object System)来进行运行时检查(包括信号,属性等),以确保转换的安全性。如果转换失败,它会返回 nullptr。

cpp 复制代码
void qobject_cast(){
    BaseClass *base = new DerivedClass;
    //基类指针转化为子类指针
     DerivedClass *derived = qobject_cast<DerivedClass*>(base);
     if (derived) {
         qDebug() << "qobject_cast succeeded";
     } else {
         qDebug() << "qobject_cast failed";
     }
     delete base;
}

上面的代码会输出

cpp 复制代码
"qobject_cast succeeded"

总结

学了这么多,上面的大家只做概念理解即可,不需要写出完整代码,面试时常问的两个问题

  1. 如何实现一个类,使其支持元对象系统(继承自QObject, 类的声明包含Q_OBJECT宏)
  2. 元对象系统有哪些特性?(信号和槽,属性系统,对象树管理,qobject_cast类型转换)
相关推荐
Laurence2 小时前
CMake 报错 Failed to find required Qt component WebEngineWidgets
qt·webengine·cmake·找不到
习惯就好zz2 小时前
Qt Quick 系统托盘完整实践
开发语言·qt·qml·系统托盘·system tray·qapplication·qguiapplication
笨笨马甲2 小时前
Qt集成OpenCV
开发语言·qt
笨笨马甲2 小时前
Qt 工业机器视觉开发
开发语言·qt
小灰灰搞电子3 小时前
Qt 打印输出:printf与qDebug的区别
开发语言·qt
火山上的企鹅3 小时前
Qt/QGroundControl 实战:接入 Skydroid(云卓) G20 遥控器 Android SDK 并实时显示摇杆与信号质量
android·开发语言·qt·qgroundcontrol·云卓sdk
白杆杆红伞伞4 小时前
Qt进程间通信
开发语言·qt
问水っ5 小时前
Qt Creator快速入门 第三版 第四章 布局管理
开发语言·qt·学习
unicrom_深圳市由你创科技6 小时前
Qt、MFC、WinForm、WPF,哪个做上位机界面更好?
qt·wpf·mfc