大家在使用Qt开发程序时,都知道怎么使用Qt的信号槽,但是Qt信号槽是怎么工作的? 大部分人仍然不知道;也就是说大家只知道怎么使用,却不知道基于什么原理,显然这是不符合深层次发展的;那么今天我就给大家基于Qt源码以及示例代码深度剖析Qt的信号槽机制,一定会对大家有帮助。
基本概念
信号
当对象改变其状态时,信号就由该对象发射 (emit) 出去,而且对象只负责发送信号,它不知道另一端是谁在接收这个信号。
槽
用于接收信号,而且槽只是普通的对象成员函数。一个槽并不知道是否有任何信号与自己相连接。
信号与槽的连接
QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal,
const QObject *receiver, const char *method,
Qt::ConnectionType type = Qt::AutoConnection)
通过QObject::connect静态函数建立连接;其中sender与receiver是指向对象的指针,signal与method分别通过SIGNAL()与SLOT()宏来进行转换。
元对象编译器
MOC, the Meta Object Compiler。
Qt程序在交由标准编译器(例如MSVC)编译之前,先使用moc分析cpp头文件;如果它发现在一个头文件中包含了Q_OBJECT宏,则会生成另外一个cpp源文件(moc_文件名.cpp),该cpp源文件中包含了Q_OBJECT宏的实现、运行时信息(反射)等。因此Qt程序的完整编译过程为moc->预处理->编译->链接
示例代码
Counter.h文件
本文福利,莬费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击莬费领取↓↓
#pragma once
#include <QtCore/QObject>
class Counter : public QObject
{
Q_OBJECT
public:
Counter();
public:
int value() const;
public slots:
void setValue(int value);
signals:
void valueChanged(int newValue);
private:
int m_value;
};
Counter.cpp文件
#include "Counter.h"
Counter::Counter():m_value(0)
{
}
int Counter::value() const
{
return m_value;
}
void Counter::setValue(int value)
{
if (value != m_value)
{
m_value = value;
emit valueChanged(value);
}
}
main.cpp文件
#include <QtCore/QCoreApplication>
#include "Counter.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Counter counterA;
Counter counterB;
QObject::connect(&counterA, SIGNAL(valueChanged(int)),
&counterB, SLOT(setValue(int)));
counterA.setValue(10);//counterA.value()=10, counterB.value()=10
counterB.setValue(20);//counterA.value()=10, counterB.value()=20
return a.exec();
}
Qt宏
宏定义在qobjectdefs.h文件中(位于QtCore目录)
-
signals
-
slots
-
Q_OBJECT
-
emit
-
SIGNAL
-
SLOT
Qt信号槽的宏使用
宏定义在qobjectdefs.h文件中(位于QtCore目录)
signals
#define signals Q_SIGNALS
#define Q_SIGNALS public QT_ANNOTATE_ACCESS_SPECIFIER(qt_signal)
#define QT_ANNOTATE_ACCESS_SPECIFIER(x)
推导出来的结果如下,即signals就是public
#define signals public
slots
#define slots Q_SLOTS
#define Q_SLOTS QT_ANNOTATE_ACCESS_SPECIFIER(qt_slot)
#define QT_ANNOTATE_ACCESS_SPECIFIER(x)
推导出来的结果如下,即slots为空
#define slots
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, "")
Q_OBJECT声明一些函数以及一个静态的QMetaObject对象, 这些函数的实现位于MOC所产生的源文件中;因此,这些函数的声明与实现就都有了;
精简版如下所示::
#define Q_OBJECT \
public: \
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_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);
emit
emit宏为一个空宏; 它甚至没有被MOC解析,换句话说,emit只是可选的,没有任何意义(除了给开发人员一个提示)。
#define emit
SIGNAL
#ifndef QT_NO_DEBUG
#define QLOCATION "\0" __FILE__ ":" QT_STRINGIFY(__LINE__)
#define SIGNAL(a) qFlagLocation("2"#a QLOCATION)
#else
#define SIGNAL(a) "2"#a
#endif
SLOT
#ifndef QT_NO_DEBUG
#define QLOCATION "\0" __FILE__ ":" QT_STRINGIFY(__LINE__)
#define SLOT(a) qFlagLocation("1"#a QLOCATION)
#else
#define SLOT(a) "1"#a
#endif
Qt信号槽宏推导归纳
#define signals public
#define slots
#define Q_OBJECT \
public: \
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_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);
#define emit
#define SIGNAL(a) "2"#a
#define SLOT(a) "1"#a
如何查看预处理之后的代码
在Visual Studio中按如下操作查看预处理之后的代码::
- 右键工程【属性】菜单,弹出属性页对话框
- 依次单击【配置属性】-->【C/C++】-->【预处理器】
- 设置【预处理到文件】选项值为“是”
- 工程编译
预处理之后代码与初始代码对比
Counter类声明对比
Counter类定义对比
main函数对比
因此,上述分析一目了然,理论推导与实践结果是高度一致的;
本文福利,莬费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击莬费领取↓↓