先放一个简单的信号槽的实现,如下:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
void sendMysignal(int a);
signals:
void mysignal(int);
public slots:
void onmysignal(int);
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(this, &MainWindow::mysignal, this, &MainWindow::onmysignal) ;
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::onmysignal(int a)
{
qDebug()<<"onmysignal a: "<<a;
}
void MainWindow::sendMysignal(int a)
{
qDebug()<<"emit mysignal a: "<<a;
emit mysignal(a);
}
由于头文件中有Q_OBJECT
,则在编译后会生成一个moc_mainwindow.cpp文件,该文件由moc生成。
Q_OBJECT
先看下Q_OBJECT
宏的定义
/* qmake ignore Q_OBJECT */
#define Q_OBJECT \
public: \
QT_WARNING_PUSH \
Q_OBJECT_NO_OVERRIDE_WARNING \
static const QMetaObject staticMetaObject; \ // 常量 QMetaObject 对象
virtual const QMetaObject *metaObject() const; \ // 用于不同的 class 返回自己的 staticMetaObject
virtual void *qt_metacast(const char *); \ // 用于转换
virtual int qt_metacall(QMetaObject::Call, int, void **); \ // 作用是查表,调用函数
QT_TR_FUNCTIONS \ // 和i18n相关
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, "")
metaObject接口
用于不同的 class 返回自己的 staticMetaObject。如果QObject实例的metaObject 不为空,则调用QObjectData::dynamicMetaObject()
,否则返回自己的staticMetaObject地址。
const QMetaObject *MainWindow::metaObject() const
{
return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}
qt_metacast接口
根据传入的类名,返回对应的实例。
void *MainWindow::qt_metacast(const char *_clname)
{
if (!_clname) return nullptr;
// 如果传入了自己,即"MainWindow",则返回this指针
if (!strcmp(_clname, qt_meta_stringdata_MainWindow.stringdata0))
return static_cast<void*>(this);
// 否则一层一层往基类调用比较,QMainWindow类也实现了Q_OBJECT,包括它上面的基本,这样一直比较到顶的QObject
return QMainWindow::qt_metacast(_clname);
}
元对象中的索引
在每一个QMetaObject对象中,槽、信号以及其它的对象可调用函数都会分配一个从0开始的索引。索引是有顺序的,信号在第一位,槽在第二位,最后是其它函数。这个索引在内部被称为相对索引,不包含父对象的索引位。为了实现包含在继承链中其它函数的索引,在相对索引的基础上添加一个偏移量,得到绝对索引。绝对索引是在公开API中使用的索引,由QMetaObject::indexOf(Signal, Slot, Method) 类似的函数返回。
多个信号槽对,会依次递增序号比如:
void Object::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
Q_ASSERT(staticMetaObject.cast(_o));
Object *_t = static_cast<Object *>(_o);
switch (_id) {
case 0: _t->ageChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
case 1: _t->scoreChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
case 2: _t->onAgeChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
case 3: _t->onScoreChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
default: ;
}
}
}
qt_metacall接口
根据Id处理。当信号发射时,QT会掉用qt_metacall,并把QMetaObject::Call设置为QMetaObject::InvokeMetaMethod。该函数同样处理属性系统,根据传入的不同的QMetaObject::Call来处理。
第二个参数是一个索引,用于唯一标识当前对象继承的类的层次结构中的信号或槽。最后一个参数是一个数组,其中包含指向参数的指针,其后是指向应放置返回值的位置(如果有)的指针,对于信号槽机制,代表则传入的参数列表。
int MainWindow::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
_id = QMainWindow::qt_metacall(_c, _id, _a); // 由于可能存在调用qt_metacall是调用基类的qt_metacall原对象,故先执行基类接口
if (_id < 0)
return _id; // 表示qt_metacall已由基类处理完毕
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;
}
enum QMetaObject::Call {
InvokeMetaMethod,
ReadProperty,
WriteProperty,
ResetProperty,
QueryPropertyDesignable,
QueryPropertyScriptable,
QueryPropertyStored,
QueryPropertyEditable,
QueryPropertyUser,
CreateInstance,
IndexOfMethod,
RegisterPropertyMetaType,
RegisterMethodArgumentMetaType
};
qt_static_metacall私有接口
void MainWindow::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
auto *_t = static_cast<MainWindow *>(_o);
Q_UNUSED(_t)
switch (_id) {
case 0: _t->mysignal((*reinterpret_cast< int(*)>(_a[1]))); break; // 调用信号函数,传入参数,该函数也由moc生成
case 1: _t->onmysignal((*reinterpret_cast< int(*)>(_a[1]))); break; // 调用槽函数,传入参数,该函数为我们自己实现
default: ;
}
} else if (_c == QMetaObject::IndexOfMethod) {
int *result = reinterpret_cast<int *>(_a[0]);
{
using _t = void (MainWindow::*)(int );
if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&MainWindow::mysignal)) {
*result = 0;
return;
}
}
}
}
void MainWindow::mysignal(int _t1)
{
void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
QMetaObject::activate(this, &staticMetaObject, 0, _a); // 0是当前对象内部信号的相对索引
}
void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index,
void **argv)
{
// 本地相对索引+偏移
int signal_index = local_signal_index + QMetaObjectPrivate::signalOffset(m);
if (Q_UNLIKELY(qt_signal_spy_callback_set.loadRelaxed()))
doActivate<true>(sender, signal_index, argv);
else
doActivate<false>(sender, signal_index, argv);
}
网友评论