Qt 的QObject底层架构、原理详细描述

首先来说一哈什么是QT的QObject?

Qobject是QT中的一个基类,它是所有QT对象的父类,它提供了一些基本的功能,比如信号和槽、属性系统、元对象系统等。如果你想让你的类具有这些功能,那么你就需要让你的类继承自Qobject,并且在类声明中加上Q_OBJECT宏。这样,moc就可以为你的类生成元对象代码,让你的类能够在运行时获取自己的元信息。

代码上,就写成下面这样:

 引入Q_OBJECT,这样就可以让定义的class类继承QObject,继而能够调用信号与槽(用的最多的机制)。

这里是官方对于Qobject的详解

在这里要注意的一点是,如果要使用QObject,需要引入头文件

#include <QObject>

这样才能使用咱们熟知的一些常用函数,connect();disconnect()等。

另外,QObject是Qt对象模型的核心。该模型的中心特征是一种非常强大的无缝对象通信机制,称为信号和槽。您可以使用connect()将信号连接到插槽,并使用disconnect()破坏连接。为了避免永不结束的通知循环,您可以使用blockSignals()临时阻止信号。受保护的函数connectNotify()和disconnectNotify()。

另外像我们常用的

Q_INVOKABLE,把C++的函数暴露给qml调用,Q_PROPERTY_AUTO定义参数给qml调用,都是基于QObject的。

由此可见,Qobject对于写QT是很重要的父类。另外还有内存管理机制,这是一种让对象拥有子对象的机制,可以让一个对象作为另一个对象的父对象。当父对象被销毁时,它会负责销毁它的所有子对象。这样保证内存不会被滥用,而且能够及时的释放内存。

以及内省(Introspection)机制,这是一种让对象自我描述的机制,可以让一个对象知道自己的类名、继承关系、方法和属性等信息。你可以使用metaObject()函数来获取一个对象的元对象信息。您可以使用 inherits() 函数确定对象的类是否继承了 QObject 继承层次结构中的另一个类。删除对象时,它会发出 destroy() 信号。您可以捕获此信号以避免悬空对 QObject 的引用。

Qt通过使用元对象(Meta Object)来实现内省机制。元对象是一个存储了对象元信息的对象,可以通过QObject::metaObject()函数来获取。Qt使用MOC(Meta Object Compiler)来生成元对象的代码,MOC是一个预处理器,它会扫描含有Q_OBJECT宏的类,并生成相应的元对象代码。元对象可以用于实现信号和槽、动态属性、动态调用等功能。

在这里,再详细的提一提信号和槽的作用:

Q的信号与槽是一种用于对象之间通信的机制,它可以实现控件之间的交互操作,比如点击按钮会使窗口关闭。信号与槽是一种高级接口,可以携带任意数量和任意类型的参数。信号与槽是Q特有的消息传输机制,需要借助一个称为moc的QT工具来正确处理。
信号是由于用户对窗口或控件进行了某些操作,导致窗口或控件产生了某个特定事件,这时候Q对应的窗口类会发出某个信号,以此对用户的操作做出反应。信号的呈现形式就是函数,也就是说某个事件产生了,Qt框架就会调用某个对应的信号函数,通知使用者。
槽是一类特殊的功能函数,它们可以作为类的普通成员函数来使用,也可以对Q框架中产生的信号进行处理。当检测到某个对象发出了某个信号,就会调用与之连接的槽函数来处理这个信号。槽函数本质是一个回调函数,调用的时机是信号产生之后,调用是Qt框架来执行的。
信号和槽可以通过Qobject类中的connect函数来连接,实现绑定。connect函数需要指定发送信号的对象、信号函数地址、接收信号的对象和槽函数地址。connect函数相当于做了信号处理动作的注册信号和槽是一种松耦合的机制,发送信号的对象不知道也不关心接收信号的对象是谁,接收信号的对象也不知道也不关心发送信号的对象是谁。只要信号和槽的签名相见配,就可以连接起来。Q的元对象系统保证了信号和槽机制的正常运行。

这里有一个简单的例子,关于QT的信号和槽

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    connect(ui->closeBtn,&QPushButton::clicked,this,&MainWindow::close);
}

MainWindow::~MainWindow()
{
    delete ui;
}

一个简单的例子是创建一个按钮,点击这个按钮关闭窗口。首先,需要在ui中添加一个button,取名为closeBtn。然后,在Mainwindow.cpp文件中,使用connect函数将按钮的clicked信号和窗口的close槽函数连接起来。这样,当用户点击按钮时,就会触发窗口的close函数,关闭窗口了。

另外再举个例子,假设我们有一个继承自QObject的Person类,它有两个属性name和gender,我们可以使用Q_PROPERTY宏来声明这两个属性,并使用Q_ENUM宏来声明gender的枚举类型。这样MOC就会为这个类生成一个元对象,我们就可以通过元对象来获取这个类的元信息,比如:

  • 使用metaObject()->className()函数来获取类名,返回"Person"。
  • 使用metaObject()->propertyCount()函数来获取属性数量,返回2。
  • 使用metaObject()->property(0)函数来获取第一个属性的元属性(QMetaProperty),然后使用name()函数来获取属性名,返回"name"。
  • 使用metaObject()->enumeratorCount()函数来获取枚举类型数量,返回1。
  • 使用metaObject()->enumerator(0)函数来获取第一个枚举类型的元枚举(QMetaEnum),然后使用keyCount()函数来获取枚举值数量,返回4。

而且我们还可以用QObject通过 event() 接收事件来过滤其他对象的事件。有关详细信息,请参阅 installEventFilter() 和 eventFilter()。可以重新实现一个方便的处理程序 childEvent() 来捕获子事件。事件在创建对象的线程中传递;有关详细信息,请参阅 Qt 和 thread() 中的线程支持。请注意,对于没有线程相关性的 QObjectthread() 返回零),根本不会执行事件处理。

使用 moveToThread() 函数更改对象及其子对象的线程相关性(如果对象具有父对象,则无法移动该对象)。最后值得一提的是,QObject在Qt中也提供了基本的定时器支持;

猜你喜欢

转载自blog.csdn.net/Helloorld_1/article/details/131894118