【元对象系统】

关键字

编译器,元对象系统,反射,信号槽

详解

在c++中提到编译器,大家直观上就认为编译器就是把代码转化为二进制的工具。这很容易让人产生误解,其实moc编译器的功能是把某些特殊的宏转化为c++代码。Qt 的 moc 会完成以下工作:

  • 展开 Q_OBJECT 宏,这个宏展开后是一系列的函数声明,moc编译器会生成moc_xxx.cpp文件,在文件内生成上述函数定义的代码;
  • 识别 Qt 中特殊的关键字,比如识别出 Q_PROPERTYQ_INVOKABLEslotsignals宏等。

下面这些关键字用于辅助moc识别信号函数和槽函数,而且必须放在头文件中:

// Qt4中为 protected. Qt5为支持connect函数指针写法, 定义为 public
#define signals public  
// slots被替换成空
#define slots
// emit被替换成空, 其作用仅仅是给qt的用户进行提示
#define emit

下面这些用于对信号和槽进行编号+命名

#define SLOT(a) "1"#a    #define SIGNAL(a) "2"#a

Q_OBJECT用于为自定义类添加一些函数声明,MOC编译器会为每个带有Q_OBJECT的头文件xxx.h自动生成moc_xxx.cpp,并在文件中实现Q_OBJECT中声明的函数。

QObject 派生的含有 Q_OBJECT 宏的类的定义必须在头文件中。

#define Q_OBJECT \
public: \
    QT_WARNING_PUSH \
    Q_OBJECT_NO_OVERRIDE_WARNING \
    static const QMetaObject staticMetaObject; \
    virtual const QMetaObject *metaObject() const; \
    virtual void *qt_metacast(const char *); \
    virtual int qt_metacall(QMetaObject::Call, int, void **); \
    QT_TR_FUNCTIONS \
private: \
    Q_OBJECT_NO_ATTRIBUTES_WARNING \
    Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \
    QT_WARNING_POP \
    struct QPrivateSignal {}; \
    QT_ANNOTATE_CLASS(qt_qobject, "")
  • staticMetaObject
    类的静态成员,存储当前类的元信息;
  • const QMetaObject *metaObject() const
    返回当前类的QMetaObject,即 staticMetaObject成员;
  • void *qt_metacast(const char *)
    QObject::inherits直接调用,用于判断是否是继承自某个类。判断时,需要传入父类的字符串名称;
  • int qt_metacall(QMetaObject::Call, int, void **)
    调用函数回调,内部调用了qt_static_metacall函数;
  • static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **)
    根据函数索引调用槽函数。

注意到有三个函数是虚函数,它们是QObject的虚函数成员,当我们继承QObject并在子类中添加Q_OBJECT宏的时候,相当于重载了这三个虚函数。

信号槽

信号槽本质就是回调函数+发布订阅模式。

Qt为提供了5种类型的回调方式,如下:
Qt::AutoConnection 自动连接,根据sender和receiver是否在一个线程里来决定使用哪种连接方式,同一个线程使用直连,否则使用队列连接
Qt::DirectConnection 直连
Qt::QueuedConnection 队列连接
Qt::BlockingQueuedConnection 阻塞队列连接,顾名思义,虽然是跨线程的,但是还是希望槽执行完之后,才能执行信号的下一步代码
Qt::UniqueConnection 唯一连接

原理

从最简单的直连,了解信号槽的实现原理。
QObject中有一个用于存储信号和槽的数据结构:

//每一个信号与一个 QObjectPrivate::ConnectionList链表关联
class QObjectConnectionListVector : public QVector<QObjectPrivate::ConnectionList>
connect

connect构造了一个QObjectPrivate::Connection 对象,然后存储在了发送者对象的ConnectionList
在这里插入图片描述

signal调用流程

signal->QMetaObject::activate->qt_static_metacall->slot
QMetaObject::activate函数内部根据ConnectionList查找到信号对应的qt_static_metacall,然后一一调用槽函数。

反射

反射:是指在运行时,能获取任意一个类对象的所有类型信息、属性、成员函数等信息的一种机制。
元对象:是指用于描述另一个对象结构的对象。

使用 Qt 反射机制的条件

1、需要继承自 QObject 类,并需要在类之中加入 Q_OBJECT 宏。
2、注册成员函数:若希望普通成员函数能够被反射,需要在函数声明之前加入
Q_INVOKABLE 宏。
3、注册成员变量:若希望成员变量能被反射,需要使用 Q_PROPERTY 宏。

原理

moc编译器会根据Q_OBJECT关键字,把类名存入 staticMetaObject静态成员中;根据 Q_INVOKABLE把方法名存入 staticMetaObject静态成员中;根据 Q_PROPERTY把属性名存入 staticMetaObject静态成员中。
注意到 staticMetaObject,它就是所谓的元对象,是静态成员,因此同一个类的所有对象共享一个元对象。

应用

类型识别

qobject_cast<>inherits()

修改属性值

QObject::setProperty()

动态IOC

QT的元对象系统,并不能直接通过new "className"的方式创建对象,需要我们自己实现。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值