QT信号槽实现原理-观察者设计模式架构-QT源码

一 、使用场景

我们使用QT的UI控件时,常用到触发控件的操作。
比如点击一个按钮,就进行一个什么操作。ui文件可以右击控件–转到槽来生成一个槽函数,很方便。
选择槽函数后,头文件里会多一个槽函数。
这是QT的 QObject函数的信号与槽功能,使得控件的点击为一个信号,点击后触发槽函数进行操作。
我们自己也可以在QObject类里写上Q_OBJECT,signals和slots来实现信号槽。
它实现的是
观察者模式:通知依赖关系,一个对象目标的状态发生改变,所有依赖对象( 观察者对象)都将得到通知。
观察者模式通过面向对象技术,弱化关系,形成稳定依赖,实现软件体系结构的松耦合。

QT的预处理-------MOC元对象编译器

信号槽需要使用QT的MOC实现,需要在类里写Q_OBJECT宏才能实现。
moc工具会解析具有Q_OBJECT宏的类,生成对应的moc_xx.cpp文件,该文件会随着项目一起编译。
Qt程序在交由标准编译器(例如MSVC)编译之前,先使用moc(MOC, 元对象编译器 the Meta Object Compiler)分析cpp头文件;如果它发现在一个头文件中包含了Q_OBJECT宏,则会生成另外一个cpp源文件(moc_文件名.cpp),该cpp源文件中包含了Q_OBJECT宏的实现、运行时信息(反射)等。因此Qt程序的完整编译过程为moc->预处理->编译->链接
QObject和QMetaObject:
顾名思义,QMetaObject包含了QObject的所谓的元数据,也就是QObject信息的一些描述信息:除了类型信息外,还包含QT中特 有的signal&slot信息。

QObject::metaObject ()方法返回一个QObject对象对应的metaobject对象,注意这个方法是virtual方法。如上文所说,如果一个类的声明中包含了 Q_OBJECT宏,编译器会生成代码来实现这个类对应的QMetaObject类,并重载QObject::metaObject()方法来返回这个 QMetaObject类的实例引用。这样当通过QObject类型的引用调用metaObejct方法时,返回的是这个引用的所指的真实对象的 metaobject。

如果一个类从QObject派生,确没有声明Q_OBJECT宏,那么这个类的metaobject对象不会被生成,这样这个类所声明的 signal slot都不能使用,而这个类实例调用metaObject()返回的就是其父类的metaobject对象,这样导致的后果就是你从这个类实例获得的元 数据其实都是父类的数据,这显然给你的代码埋下隐患。因此如果一个类从QOBject派生,它都应该声明Q_OBJECT宏,不管这个类有没有定义 signal&slot和Property。

这样每个QObject类都有一个对应的QMetaObject类,形成一个平行的类型层次。

信号和槽实现类似设计模式的观察者模式

观察者模式定义:
定义对象间的一种一对多变化的依赖关系,以便当一个对象(subject)的状态发送变化时,所有依赖于它的对象都得到通知并自动更新。
动机:
在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系” ----一个对象(目标对象)的状态发生改变,所以的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。
使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。
目的都是去耦合。信号槽是将这种模式封装好了,方便我们调用。实现了 对接口编程而不是对实现编程.从抽象类QObject类继承的类都共享其接口(signals slots).

二 、信号与槽实现及源码

我们定义一个发送信号的sender发送者
发送信号operate(),
在这里插入图片描述
和一个接收槽函数的接收者receiver
在这里插入图片描述
信号 :当按钮(对象)改变状态时,信号就emit,对象只负责发送,不负责接收方的处理。
槽: 接收到信号,不关心信号。
信号与槽如何连接:
原来QT是通过QObject::connect静态函数建立连接;其中sender与receiver是指向对象的指针,分别代表了被观察者和 观察者。
signal与method分别通过SIGNAL()与SLOT()宏来进行转换 (类型是const char*)
他们通过 connect(this, &myclass::operate, worker, &Worker::doWork);绑定信号和槽函数。

宏定义 -moc文件

QObject类型有 signals slots Q_OBJECT emit SIGNAL SLOT 等信号槽相关的宏定义。

CTRL键+鼠标点击这些宏,进入qobjecdefs.h文件,这里定义了这些宏。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

信号 moc预处理

首先 emit 信号 发送了什么?(注:emit是个空的宏)
moc_xxx.cpp文件中看到
发送者的信号函数里有 QMetaObject::activate


// SIGNAL 0
void myclass::operate(const QString & _t1)
{
    void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
    QMetaObject::activate(this, &staticMetaObject, 0, _a);
}

其定义在

qobjectdefs.h文件里

    // internal index-based signal activation
    static void activate(QObject *sender, int signal_index, void **argv);
    static void activate(QObject *sender, const QMetaObject *, int local_signal_index, void **argv);
    static void activate(QObject *sender, int signal_offset, int local_signal_index, void **argv);

槽moc预处理

**槽函数呢 **在moc_xxx文件里 找到槽函数相关的函数 后面会讲如何和信号绑定 的如何触发 的。

void Worker::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod) {
        auto *_t = static_cast<Worker *>(_o);
        Q_UNUSED(_t)
        switch (_id) {
        case 0: _t->resultReady((*reinterpret_cast< const QString(*)>(_a[1]))); break;
        case 1: _t->doWork((*reinterpret_cast< const QString(*)>(_a[1]))); break;
        default: ;
        }
    } else if (_c == QMetaObject::IndexOfMethod) {
        int *result = reinterpret_cast<int *>(_a[0]);
        {
            using _t = void (Worker::*)(const QString & );
            if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&Worker::resultReady)) {
                *result = 0;
                return;
            }
        }
    }
}

发送者和接收者都继承QObject类,
重写了QObject::qt_metacall 调用了qt_static_metacall()
qt_static_metacall会将槽函数调用

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

QMetaObject 内容- -

QObject里有 静态成员变量
static const QMetaObject staticQtMetaObject;
在这里插入图片描述
moc文件里看出来预处理对应的worker 的QMetaObject 的成员变量是

QT_INIT_METAOBJECT const QMetaObject Worker::**staticMetaObject** = { {
    &QObject::staticMetaObject,
    qt_meta_stringdata_Worker.data,
    qt_meta_data_Worker,
    qt_static_metacall,
    nullptr,
    nullptr
} };

其中成员 qt_meta_stringdata_Worker.data,为

static const qt_meta_stringdata_Worker_t qt_meta_stringdata_Worker = {
    {
QT_MOC_LITERAL(0, 0, 6), // "Worker"
QT_MOC_LITERAL(1, 7, 11), // "resultReady"
QT_MOC_LITERAL(2, 19, 0), // ""
QT_MOC_LITERAL(3, 20, 7), // "result1"
QT_MOC_LITERAL(4, 28, 6), // "doWork"
QT_MOC_LITERAL(5, 35, 9) // "parameter"

    },
    "Worker\0resultReady\0\0result1\0doWork\0"
    "parameter"
};

可以看出是Worker类的名称、信号名称、槽函数名称及参数等字符串。
在这里插入图片描述
connect有什么呢?
点击进去
connect函数是QObject类的成员函数 返回值是QMetaObject::Connection

    static QMetaObject::Connection connect(const QObject *sender, const char *signal,
                        const QObject *receiver, const char *member, Qt::ConnectionType = Qt::AutoConnection);

    static QMetaObject::Connection connect(const QObject *sender, const QMetaMethod &signal,
                        const QObject *receiver, const QMetaMethod &method,
                        Qt::ConnectionType type = Qt::AutoConnection);

    inline QMetaObject::Connection connect(const QObject *sender, const char *signal,
                        const char *member, Qt::ConnectionType type = Qt::AutoConnection) const;

所以QObject 里实现connect函数
QMetaObject 实现Connection 类是connect的返回类型

class Q_CORE_EXPORT QMetaObject::Connection {
    void *d_ptr; //QObjectPrivate::Connection*
    explicit Connection(void *data) : d_ptr(data) {  }
    friend class QObject;
    friend class QObjectPrivate;
    friend struct QMetaObject;
    bool isConnected_helper() const;
public:
    ~Connection();
    Connection();
    Connection(const Connection &other);
    Connection &operator=(const Connection &other);
#ifdef Q_QDOC
    operator bool() const;
#else
    typedef void *Connection::*RestrictedBool;
    operator RestrictedBool() const { return d_ptr && isConnected_helper() ? &Connection::d_ptr : nullptr; }
#endif

    Connection(Connection &&o) Q_DECL_NOTHROW : d_ptr(o.d_ptr) { o.d_ptr = nullptr; }
    Connection &operator=(Connection &&other) Q_DECL_NOTHROW
    { qSwap(d_ptr, other.d_ptr); return *this; }
};

三、实现

实现及架构–看这里----->

  • 3
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值