Qt 信号与槽研究2

一个信号对槽的调用: 会比直接函数调用耗费更多的时间/空间;
但是,信号与槽实现了对象之间的松耦合. 使用灵活,方便。比较符合人的思维方式。

这就是以易用性来换取时间或空间。

下面实例讲解QT signal, slot, connect, emit 的含义。

先上代码: 最简单代码,我不喜欢复杂的。

两个类,一个A, 一个B, 声明两个对象,一个a,一个b. a 发信号调用b的槽函数。

$ cat a.h
#include <QObject>  

class A : public QObject  
{  
Q_OBJECT  
public:  
	A(QObject* parent = 0){(void)parent;} // 空构造
	void aFunc();  // 在这个函数里发射信号
signals :  
	void SendString(QString msg);  
	void SendInt(int i);  
};  
$ cat a.cpp
#include "a.h"
void A::aFunc()
{
	emit SendString("this is a str");
	emit SendInt(8);
}

B 类声明和实现

cat b.h
#include <QObject>  
class B : public QObject  
{  
Q_OBJECT  
	public:  
		B(QObject* parent = 0){(void)parent;};  
	public slots:  
		void ReceiveString(QString msg);  
		void ReceiveOtherSlot(float f);
		void ReceiveInt(int i);  
};  
$ cat b.cpp
#include <QDebug>
#include "b.h"

void B::ReceiveString(QString msg)
{
	qDebug() << msg <<endl;
}

void B::ReceiveOtherSlot(float f)
{
	qDebug() << f <<endl;
}
void B::ReceiveInt(int i)
{
	qDebug() << i <<endl;
}

测试程序:

$ cat main.cpp
#include <QApplication>  
#include "a.h"
#include "b.h"
  
int main(int argc, char *argv[])  
{  
    QApplication app(argc, argv);
    A a;
    B b;
    QObject::connect(&a, SIGNAL(SendString(QString)), &b, SLOT(ReceiveString(QString)));
    QObject::connect(&a, SIGNAL(SendInt(int)), &b, SLOT(ReceiveInt(int)));
    a.aFunc();  // 在这个函数中发射信号
    return app.exec();  
}       

解释一下几个概念:
signals 是什么?
    signals 是一系列信号,
    实际上是一个函数原型声明,但是没有函数实现。
    signals 无需声明公有,私有属性.
    实际上,函数实现在其它对象中,什么样子不关心。
    发射一个信号,就是调用一个或多个其它对象中的函数。
    到底哪些函数被调用,是由QObject::connect() 所决定

slots 是什么?
    slots 是一系列槽函数. 槽函数与普通函数没有区别,
    只是它可以接受信号,可以被其它对象所调用。
    slots 具有public,private,protect 属性,决定其可连接属性

connect 是什么?
connect 是QObject 一个函数,用以登记两个对象的连接属性,它使用的SIGNAL 是一个宏
        用于将后面的函数变换为字符串, SLOT 也是一个宏,用于将后面函数变换为字符串,

        使得QOjbect可以登记 两个函数名称。 将一个对象的信号于另一个对象的槽函数联系起来。

 注意: connect 连接的信号和槽是函数原型, 函数参数不用带形参名称,否则连接不上,会提示无此信号或槽。


signal 的使用
----------------------------------------
emit SendInt(8);
----------------------------------------
1. emit 是什么?, emit 是个宏,空的宏, #define emit ""
2. signal 如何调用到slot 函数

用gdb 调试这个程序.  我们在 B::ReceiveInt 设置断点, 断下后:
(gdb) bt
  #0  B::ReceiveInt (this=0x7fffffffdc80, i=8) at b.cpp:15
  #1  0x0000000000402575 in B::qt_static_metacall (_o=0x7fffffffdc80, _c=QMetaObject::InvokeMetaMethod, _id=2, _a=0x7fffffffdbf0) at      moc_b.cpp:53
  #2  0x00007ffff6a31fdf in QMetaObject::activate (sender=0x7fffffffdc70, m=0x402840 <A::staticMetaObject>, local_signal_index=1,         argv=0x7fffffffdbf0) at kernel/qobject.cpp:3539
  #3  0x00000000004023f4 in A::SendInt (this=0x7fffffffdc70, _t1=8) at moc_a.cpp:106
  #4  0x00000000004018bc in A::aFunc (this=0x7fffffffdc70) at a.cpp:5


从底向上分析:
1.  在a.cpp:5, 调用了    
    emit SendInt(8); 对应于:
    A::SendInt (this=0x7fffffffdc70, _t1=8) , 这实际上是moc_a.cpp 中的一个函数,属于A 类
2. 在 moc_a.cpp:106, 调用了
    QMetaObject::activate(this, &staticMetaObject, 1, _a); 对应于: kernel/qobject.cpp:3539
    QMetaObject::activate (sender=0x7fffffffdc70, m=0x402840 <A::staticMetaObject>, local_signal_index=1, argv=0x7fffffffdbf0)
    该cpp 文件定义了QObject, QMetaObject 类的几个成员函数。函数原型
    void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index, void **argv)
    可以看到sender 是a, QMetaObject 是A::staticMetaObject, local_signal_index 是1,并带有参数传给了该函数。

3. 在 kernel/qobject.cpp:3539, 调用了
    callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv ? argv : empty_argv);
    对应于
    B::qt_static_metacall (_o=0x7fffffffdc80, _c=QMetaObject::InvokeMetaMethod, _id=2, _a=0x7fffffffdbf0)
    函数原型:void B::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)

    可见,QMetaObject 类负责寻径,找到receiver 对象为 b, 类型QMetaObject::InvokeMetaMethod, 接受_id 为2, 带参数.
    它是怎么找的,当然是QObject::connect 注册过的。
    这个被调用函数, 在moc_b.cpp 中。

4.  在moc_b.cpp:53 调用了
    case 2: _t->ReceiveInt((*reinterpret_cast< int(*)>(_a[1]))); break;
    对应于
    B::ReceiveInt (this=0x7fffffffdc80, i=8)
    函数原型: void B::ReceiveInt(int i)
    可见,该函数通过参数_id=2, 找到了正确的槽函数。并传递参数调用槽函数。

简单梳理一下: 对象a(signal)->moc_a->qobject->moc_b->b(SLOT).对象b(slots)

两边是moc辅助函数, 中间夹着qobject类, qobject 根据注册的connect 函数负责send, receive的钩挂和寻径

至此,路径分析就算清楚了。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值