QT之信号与槽

2 篇文章 0 订阅

文章目录


QT的信号与槽机制主要是为了实现不同部件之间的信息交互,即一个部件状态发生了变化,希望相关的部件能获知该变化并做出响应。那么前者可在状态变化时发射一个信号,则与该信号连接了的槽函数都会被按连接顺序依次调用。与回调(callback)机制不同的是,信号与槽机制实现了发布者和订阅者的解耦合,即发布者不知道也无需知道谁会捕获发射的信号,订阅者亦然。
尝试搞明白QT的信号与槽机制底层是怎么实现的,但是感觉只是触及到了一些比较核心的概念,完整的逻辑还是没能完全搞明白。 主要欠缺的部分是信号和槽是怎么联系起来的,文中做了些猜想。虽不完整,还是记录一下,以便后续能继续探索。

meta-object system

Qt’s meta-object system provides the signals and slots mechanism for inter-object communication, run-time type information, and the dynamic property system.

QT的信号与槽机制是由其元对象系统(meta-object system)提供的,元对象系统主要基于以下三点:

  1. QObject:只有该类及该类的派生类才能使用元对象系统。
  2. Q_OBJECT:在QObject类及其派生类中使用元对象系统,必须在类的开始位置调用Q_OBJECT宏,该宏会提供一系列的元对象系统相关的声明(QMetaObject),以支持信号与槽机制、运行时类型信息和动态参数系统。
    例如定义如下类:
	#ifndef TEXTFINDER_H
	#define TEXTFINDER_H

	#include <QWidget>

	QT_BEGIN_NAMESPACE
	namespace Ui { class TextFinder; }
	QT_END_NAMESPACE

	class TextFinder : public QWidget
	{
    	Q_OBJECT

	public:
    	TextFinder(QWidget *parent = nullptr);
    	void metaInfo();
   	 ~TextFinder();

	private slots:
    	void on_findButton_clicked();

	private:
    	Ui::TextFinder *ui;
    	void loadTextFile();
	};
	#endif // TEXTFINDER_H

其中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, "")
  1. moc(Meta-Object Compiler):QT提供的元对象编译工具,该工具识别到类中的Q_OBJECT宏后,会为上面Q_OBJECT宏展开的声明提供定义,文件名为moc_<classname>.h,本例生成的改文件内容为:
/****************************************************************************
** Meta object code from reading C++ file 'textfinder.h'
**
** Created by: The Qt Meta Object Compiler version 67 (Qt 5.9.9)
**
** WARNING! All changes made in this file will be lost!
*****************************************************************************/

#include "../../TextFinder/textfinder.h"
#include <QtCore/qbytearray.h>
#include <QtCore/qmetatype.h>
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'textfinder.h' doesn't include <QObject>."
#elif Q_MOC_OUTPUT_REVISION != 67
#error "This file was generated using the moc from 5.9.9. It"
#error "cannot be used with the include files from this version of Qt."
#error "(The moc has changed too much.)"
#endif

QT_BEGIN_MOC_NAMESPACE
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
struct qt_meta_stringdata_TextFinder_t {
    QByteArrayData data[3];
    char stringdata0[34];
};
#define QT_MOC_LITERAL(idx, ofs, len) \
    Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
    qptrdiff(offsetof(qt_meta_stringdata_TextFinder_t, stringdata0) + ofs \
        - idx * sizeof(QByteArrayData)) \
    )
static const qt_meta_stringdata_TextFinder_t qt_meta_stringdata_TextFinder = {
    {
QT_MOC_LITERAL(0, 0, 10), // "TextFinder"
QT_MOC_LITERAL(1, 11, 21), // "on_findButton_clicked"
QT_MOC_LITERAL(2, 33, 0) // ""

    },
    "TextFinder\0on_findButton_clicked\0"
};
#undef QT_MOC_LITERAL

static const uint qt_meta_data_TextFinder[] = {

 // content:
       7,       // revision
       0,       // classname
       0,    0, // classinfo
       1,   14, // methods
       0,    0, // properties
       0,    0, // enums/sets
       0,    0, // constructors
       0,       // flags
       0,       // signalCount

 // slots: name, argc, parameters, tag, flags
       1,    0,   19,    2, 0x08 /* Private */,

 // slots: parameters
    QMetaType::Void,

       0        // eod
};

void TextFinder::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod) {
        TextFinder *_t = static_cast<TextFinder *>(_o);
        Q_UNUSED(_t)
        switch (_id) {
        case 0: _t->on_findButton_clicked(); break;
        default: ;
        }
    }
    Q_UNUSED(_a);
}

const QMetaObject TextFinder::staticMetaObject = {
    { &QWidget::staticMetaObject, qt_meta_stringdata_TextFinder.data,
      qt_meta_data_TextFinder,  qt_static_metacall, nullptr, nullptr}
};


const QMetaObject *TextFinder::metaObject() const
{
    return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}

void *TextFinder::qt_metacast(const char *_clname)
{
    if (!_clname) return nullptr;
    if (!strcmp(_clname, qt_meta_stringdata_TextFinder.stringdata0))
        return static_cast<void*>(this);
    return QWidget::qt_metacast(_clname);
}

int TextFinder::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
    _id = QWidget::qt_metacall(_c, _id, _a);
    if (_id < 0)
        return _id;
    if (_c == QMetaObject::InvokeMetaMethod) {
        if (_id < 1)
            qt_static_metacall(this, _c, _id, _a);
        _id -= 1;
    } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
        if (_id < 1)
            *reinterpret_cast<int*>(_a[0]) = -1;
        _id -= 1;
    }
    return _id;
}
QT_WARNING_POP
QT_END_MOC_NAMESPACE

可以看到moc对Q_OBJECT的声明提供了函数定义,其中的函数功能分别为:

  • qt_static_metacall:类的私有函数,真正调用槽函数的位置,其参数_id决定了调用哪个槽函数。我猜想将信号与槽进行connect的时候,会维护一个键值对,分别是整数表示的信号与槽的index。
  • qt_metacall:公有函数,在这里会调用qt_static_metacall
  • metaObject: 返回一个包含该类元信息的元对象的指针,即TextFinder::staticMetaObject,该对象也是moc构造的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值