做污事网站,园林景观设计公司,网站开发应用到的技术名词,wordpress 调用百度地图Qt的元对象系统#xff08;Meta-Object System#xff09;是Qt框架的核心功能之一#xff0c;为C语言增加了一些动态特性#xff0c;借助元对象系统Qt可以实现以下功能
信号与槽机制#xff08;Signals and Slots#xff09;运行时类型信息#xff08;Run-Time Type In…Qt的元对象系统Meta-Object System是Qt框架的核心功能之一为C语言增加了一些动态特性借助元对象系统Qt可以实现以下功能
信号与槽机制Signals and Slots运行时类型信息Run-Time Type Information, RTTI属性系统Property System动态对象创建Dynamic Object Creation动态方法调用Dynamic Method Invocation安全的类型转换(Dynamic Cast)
元对象系统的实现需要借助moc (Meta-Object_Compiler)工具 moc工具扫描包含Q_OBJECT宏的头文件为为我们自动生成了元对象系统的实现代码我们以一个简单的MainWindow例子来探究一下moc系统有什么黑魔法示例完整代码放在文章最后。
QObject派生类的元信息编码和存储
元(Meta)在C中是和编译期相关的概念元对象用来存储一个类在编译器就确定的类型相关信息 即每一个类的类型和方法信息都是编译期都是已经确定了的在一般自己实现C反射系统的方案中存储类的元信息无不例外的都是借助一个静态成员变量由于静态变量是存储在静态存储区生命周期伴随整个程序并且其初始化是在程序加载后main入口函数之前所以用一个静态类来存储元信息便很自然了如下Qt中就是给每一个定义Q_OBJECT宏的类定义了一个static QMetaObject成员对象。 staticMetaObject这个静态成员的定义就在moc工具自动生成的moc_mainwindow.cpp文件之中其中主要包含类的派生信息和信号与槽函数信息。 qt_meta_stringdata_MainWindow和qt_meta_data_MainWindow的定义在下面两张截图之中我们先来看一下qt_meta_stringdata_MainWindow这个结构里面将信号和槽函数的信息编码存储到了一个字符串之中QT_MOC_LITERAL(0, 0, 10)中第一个参数为编号第二个为在下面字符串中的起始位置第二个参数为函数名称的长度。 qt_meta_data_MainWindow这个结构指定了每个信号和槽函数在上面的“字符串”中的位置以及函数的返回值参数类型信息这两个结构就编码存储了信号槽函数的所有信息了。 槽函数的调用
这里先讨论一个问题如果知道一个函数的字符串名称怎么调用这个函数 注意这里的问题是你现在只知道一个字符串 虽然你知道它是那个函数但是你怎么来调用
std::string func_name my_slot;
//how to call my_slot() ?
func_name()? // this is error~//the possiable way
if(func_name my_slot){my_slot();
} else if( func_name my_slot1){my_slot1();
}
在老版本的Qt中connect链接信号和槽的时候也是直接连接的函数字符串名称通过字符串来调用函数就不得不借用我们moc文件里面生成的qt_static_metacall了这里的实现也是很明了首先找到函数的索引然后通过switch case进行函数调用。
void MainWindow::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{if (_c QMetaObject::InvokeMetaMethod) {auto *_t static_castMainWindow *(_o);Q_UNUSED(_t)switch (_id) {case 0: _t-my_signal(); break;case 1: _t-my_signal_param((*reinterpret_cast int(*)(_a[1])),(*reinterpret_cast double(*)(_a[2]))); break;case 2: _t-on_actionExit_triggered(); break;case 3: _t-my_slot(); break;case 4: _t-my_slot_param((*reinterpret_cast int(*)(_a[1]))); break;default: ;}} else if (_c QMetaObject::IndexOfMethod) {int *result reinterpret_castint *(_a[0]);{using _t void (MainWindow::*)();if (*reinterpret_cast_t *(_a[1]) static_cast_t(MainWindow::my_signal)) {*result 0;return;}}{using _t void (MainWindow::*)(int , double );if (*reinterpret_cast_t *(_a[1]) static_cast_t(MainWindow::my_signal_param)) {*result 1;return;}}}
}
信号函数的实现
我们在定义信号的时候只给出了一个信号函数的申明信号函数的实现也是moc自动生成的
// SIGNAL 0
void MainWindow::my_signal()
{QMetaObject::activate(this, staticMetaObject, 0, nullptr);
}// SIGNAL 1
void MainWindow::my_signal_param(int _t1, double _t2)
{void *_a[] { nullptr, const_castvoid*(reinterpret_castconst void*(std::addressof(_t1))), const_castvoid*(reinterpret_castconst void*(std::addressof(_t2))) };QMetaObject::activate(this, staticMetaObject, 1, _a);
}
总结
我们可以发现QT的元对象系统并没有什么神奇之处主要干了下面几件事情
通过一个静态成员QMetaObject存储类的元信息通过一个字符串编码存储了信号和槽函数信息通过信号和槽函数的索引在switch case中调用槽函数自动生成信号函数的实现代码
mainwindow示例
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include QMainWindownamespace Ui {
class MainWindow;
}class MainWindow : public QMainWindow
{Q_OBJECTpublic:explicit MainWindow(QWidget *parent 0);explicit MainWindow(QWidget* parent, int id);~MainWindow();void my_general_fun();private slots:void on_actionExit_triggered();void my_slot();void my_slot_param(int iiii);signals:void my_signal();void my_signal_param(int jjjj, double kkkk);private:Ui::MainWindow *ui;
};#endif // MAINWINDOW_Hmoc生成的实现代码
/****************************************************************************
** Meta object code from reading C file mainwindow.h
**
** Created by: The Qt Meta Object Compiler version 67 (Qt 5.15.2)
**
** WARNING! All changes made in this file will be lost!
*****************************************************************************/#include memory
#include ../../../../mainwindow.h
#include QtCore/qbytearray.h
#include QtCore/qmetatype.h
#if !defined(Q_MOC_OUTPUT_REVISION)
#error The header file mainwindow.h doesnt include QObject.
#elif Q_MOC_OUTPUT_REVISION ! 67
#error This file was generated using the moc from 5.15.2. It
#error cannot be used with the include files from this version of Qt.
#error (The moc has changed too much.)
#endifQT_BEGIN_MOC_NAMESPACE
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
struct qt_meta_stringdata_MainWindow_t {
QByteArrayData data[10];
char stringdata0[99];
};
#define QT_MOC_LITERAL(idx, ofs, len) \
Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
qptrdiff(offsetof(qt_meta_stringdata_MainWindow_t, stringdata0) ofs \
- idx * sizeof(QByteArrayData)) \
)
static const qt_meta_stringdata_MainWindow_t qt_meta_stringdata_MainWindow {
{
QT_MOC_LITERAL(0, 0, 10), // MainWindow
QT_MOC_LITERAL(1, 11, 9), // my_signal
QT_MOC_LITERAL(2, 21, 0), //
QT_MOC_LITERAL(3, 22, 15), // my_signal_param
QT_MOC_LITERAL(4, 38, 4), // jjjj
QT_MOC_LITERAL(5, 43, 4), // kkkk
QT_MOC_LITERAL(6, 48, 23), // on_actionExit_triggered
QT_MOC_LITERAL(7, 72, 7), // my_slot
QT_MOC_LITERAL(8, 80, 13), // my_slot_param
QT_MOC_LITERAL(9, 94, 4) // iiii},
MainWindow\0my_signal\0\0my_signal_param\0
jjjj\0kkkk\0on_actionExit_triggered\0
my_slot\0my_slot_param\0iiii
};
#undef QT_MOC_LITERALstatic const uint qt_meta_data_MainWindow[] {
// content:
8, // revision
0, // classname
0, 0, // classinfo
5, 14, // methods
0, 0, // properties
0, 0, // enums/sets
0, 0, // constructors
0, // flags
2, // signalCount// signals: name, argc, parameters, tag, flags
1, 0, 39, 2, 0x06 /* Public */,
3, 2, 40, 2, 0x06 /* Public */,// slots: name, argc, parameters, tag, flags
6, 0, 45, 2, 0x08 /* Private */,
7, 0, 46, 2, 0x08 /* Private */,
8, 1, 47, 2, 0x08 /* Private */,// signals: parameters
QMetaType::Void,
QMetaType::Void, QMetaType::Int, QMetaType::Double, 4, 5,// slots: parameters
QMetaType::Void,
QMetaType::Void,
QMetaType::Void, QMetaType::Int, 9,0 // eod
};void MainWindow::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{if (_c QMetaObject::InvokeMetaMethod) {auto *_t static_castMainWindow *(_o);Q_UNUSED(_t)switch (_id) {case 0: _t-my_signal(); break;case 1: _t-my_signal_param((*reinterpret_cast int(*)(_a[1])),(*reinterpret_cast double(*)(_a[2]))); break;case 2: _t-on_actionExit_triggered(); break;case 3: _t-my_slot(); break;case 4: _t-my_slot_param((*reinterpret_cast int(*)(_a[1]))); break;default: ;}} else if (_c QMetaObject::IndexOfMethod) {int *result reinterpret_castint *(_a[0]);{using _t void (MainWindow::*)();if (*reinterpret_cast_t *(_a[1]) static_cast_t(MainWindow::my_signal)) {*result 0;return;}}{using _t void (MainWindow::*)(int , double );if (*reinterpret_cast_t *(_a[1]) static_cast_t(MainWindow::my_signal_param)) {*result 1;return;}}}
}QT_INIT_METAOBJECT const QMetaObject MainWindow::staticMetaObject { {QMetaObject::SuperData::linkQMainWindow::staticMetaObject(),qt_meta_stringdata_MainWindow.data,qt_meta_data_MainWindow,qt_static_metacall,nullptr,nullptr
} };const QMetaObject *MainWindow::metaObject() const
{return QObject::d_ptr-metaObject ? QObject::d_ptr-dynamicMetaObject() : staticMetaObject;
}void *MainWindow::qt_metacast(const char *_clname)
{if (!_clname) return nullptr;if (!strcmp(_clname, qt_meta_stringdata_MainWindow.stringdata0))return static_castvoid*(this);return QMainWindow::qt_metacast(_clname);
}int MainWindow::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{_id QMainWindow::qt_metacall(_c, _id, _a);if (_id 0)return _id;if (_c QMetaObject::InvokeMetaMethod) {if (_id 5)qt_static_metacall(this, _c, _id, _a);_id - 5;} else if (_c QMetaObject::RegisterMethodArgumentMetaType) {if (_id 5)*reinterpret_castint*(_a[0]) -1;_id - 5;}return _id;
}// SIGNAL 0
void MainWindow::my_signal()
{QMetaObject::activate(this, staticMetaObject, 0, nullptr);
}// SIGNAL 1
void MainWindow::my_signal_param(int _t1, double _t2)
{void *_a[] { nullptr, const_castvoid*(reinterpret_castconst void*(std::addressof(_t1))), const_castvoid*(reinterpret_castconst void*(std::addressof(_t2))) };QMetaObject::activate(this, staticMetaObject, 1, _a);
}
QT_WARNING_POP
QT_END_MOC_NAMESPACE