Qt之元对象系统
Qt是一个功能强大的跨平台C++ 框架,其核心特性之一就是元对象系统(Meta-Object System)。它使得Qt能够在标准C++的基础上,实现诸如信号与槽(Signals & Slots)、运行时类型信息(RTTI)、动态属性(Dynamic Properties)以及方法动态调用等高级功能。
Qt元对象系统包含有三个关键组件:QObject类、Q_OBJECT宏以及元对象编译器MOC(Meta-Object Compiler)。
文章目录
-
-
- QObject类
-
- 信号和槽
- 事件系统
- 动态属性
- 对象树和内存管理
- 线程亲和性
- 国际化
- Q_OBJECT宏
- 元对象编译器MOC
-
- Qt使用MOC的原因
- moc文件的使用方式
- 常见构建错误
-
QObject类
QObject类是Qt框架的核心基类之一,所有从QWidget、QTimer、QThread等派生的类都依赖于QObject的事件机制。所有使用元对象系统的类都必须继承(直接或间接)自QObject。
信号和槽
QObject是信号和槽的基础,通过signals和slots关键字,对象间可以实现通信。发射对象发出信号,在接收对象中处理信号对应的事件:
class Button : public QObject {Q_OBJECTpublic:void click() { emit clicked(); }signals:void clicked();};class Handler : public QObject {Q_OBJECTpublic slots:void onButtonClicked() { qDebug() << "Button clicked!"; } };Button btn;Handler handler;QObject::connect(&btn, &Button::clicked, &handler, &Handler::onButtonClicked);//handler对接接收到btn发出的clicked信号后,会执行onButtonClicked()函数
Qt元对象系统还提供了一个静态辅助函数QMetaObject::connectSlotsByName(QObject *object) 用于自动将信号连接到符合命名规范的槽函数。它通过约定命名规则的方式(如下所示),将对象中符合特定命名格式的槽函数自动连接到对应发送者的信号上。它常用于简化信号与槽的手动连接.
void on_<object name>_<signal name>(<signal parameters>);
<object name>:指在UI文件(如 .ui)或代码中为该控件设置的objectName。<signal name>:信号的名称(不含signal后缀,如clicked而不是clicked())。- 参数类型和数量必须与信号完全匹配
原理:
- 遍历object的所有子对象(通过 findChild() 或父子关系);
- 查找这些子对象发出的信号;
- 在 object 自身类中查找符合命名规则的槽函数;
- 如果找到匹配的槽,则自动调用
connect()建立连接。
如你在mainwindow.ui文件中添加(通过Qt Designer工具)了一个PushButton按钮(object name为pushButton)
// mainwindow.h
class MainWindow : public QMainWindow {Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);private slots:void on_pushButton_clicked(); // ← 符合命名规则
};
// mainwindow.cpp
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)
{ui = new Ui::MainWindow;ui->setupUi(this); // 此时 pushButton 已作为 this 的子对象存在QMetaObject::connectSlotsByName(this); // 自动连接 on_pushButton_clicked()
}
关于信号和槽后续博客会有详细介绍,请持续关注。
事件系统
QObject可以通过event()接收事件,并能过滤其他对象的事件。例如可以重写QTextEdit的event函数,修改回车键事件使其不换行:
class MyTextEdit : public QTextEdit{Q_OBJECTprotected:bool event(QEvent* e) override {if (e->type() == QEvent::KeyPress) {QKeyEvent* keyEvent = dynamic_cast<QKeyEvent*>(e);if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return) {qDebu