一、QObject核心定位QObject是Qt所有可支持信号槽、内存自动管理、事件循环、元对象系统类的基类位于QObject头文件。几乎所有Qt控件、网络、线程、定时器等组件都直接/间接继承自它。核心特性全部依赖元对象系统MOC信号与槽Signal SlotQt跨对象通信机制父子内存管理父对象销毁自动回收所有子对象元对象反射运行时获取类名、属性、方法、枚举事件系统接收、分发、过滤事件对象名称动态属性objectName、setProperty跨线程信号槽支持多线程安全通信二、使用前置必备moc元对象宏1. Q_OBJECT宏只要类需要信号槽、属性、元对象反射类定义内第一行必须写Q_OBJECT否则moc不会生成元代码信号槽直接失效。#include QObject class MyObj : public QObject{ Q_OBJECT // 关键宏放在类内部开头public: explicit MyObj(QObject *parent nullptr);};注意事项:(1) Q_OBJECT只能在继承QObject的类中使用(2) 类必须是public继承QObject(3) qmake/CMake会自动调用moc扫描带Q_OBJECT的头文件生成.moc源码(4) 忘记加会报undefined reference to vtable链接错误2. 其他配套元对象宏宏作用Q_PROPERTY定义可反射属性支持QML /脚本读写Q_SIGNALS标记信号函数段可省略仅可读性Q_SLOTS标记槽函数可省略Q_ENUM导出枚举到元对象运行时转字符串Q_INVOKABLE普通成员函数可通过metaObject()-invokeMethod()动态调用三、父子对象与内存自动管理最常用特性1. 构造函数 parent 参数所有 QObject 子类标准构造explicit MyObj(QObject *parent nullptr);parent为父QObject对象指针创建时传入父对象自动加入父对象子对象列表2. 销毁规则核心父对象析构时会自动delete所有子对象递归销毁无需手动释放内存杜绝内存泄漏。示例// 栈父对象出作用域自动销毁子对象 void test(){ QObject parentObj; // child1、child2 归属 parentObj QObject *child1 new QObject(parentObj); QObject *child2 new QObject(child1); // 函数结束 parentObj 析构 → 先删 child1 → 再删 child2 }3. 常用父子操作API// 设置父对象 void setParent(QObject *parent); // 获取父对象 QObject *parent() const; // 获取所有一级子对象列表 QListQObject* children() const; // 根据 objectName 查找子对象递归查找 templatetypename T T findChild(const QString name QString(), Qt::FindChildOptions options Qt::FindDirectChildrenOnly); QListT findChildren(...)findChild实用场景界面中根据名称查找控件、动态管理子业务对象QPushButton *btn parentWidget-findChildQPushButton(btn_submit);4. 手动释放注意不要栈对象当父子对象堆创建后手动delete父会双重释放崩溃多线程父子不能跨线程父和子必须在同一线程跨线程会警告内存异常窗口控件QWidget自带父子管理布局控件会自动设置 parent四、信号与槽SignalSlotQObject核心通信1. 信号signals关键字 signals: 下声明无实现、无返回值、权限默认 protected触发emit 信号名(参数);class MyObj : public QObject{ Q_OBJECT signals: void sendMsg(const QString text); // 信号只声明不写函数体public: void testEmit() { emit sendMsg(hello qobject); // 发射信号 }};2. 槽slots三种合法槽函数(1) public slots: / private slots: 显式标记(2) 普通 public 成员函数Qt5 新 connect 语法支持(3) 标记 Q_INVOKABLE 的函数class Receiver : public QObject{ Q_OBJECTpublic slots: void onRecv(const QString msg) { qDebug() 收到 msg; }};3. connect四种写法Qt4老式/Qt5新式推荐方式 1Qt5函数指针推荐编译期类型检查MyObj sender; Receiver recv;// connect(发送者, 信号地址, 接收者, 槽地址) connect(sender, MyObj::sendMsg, recv, Receiver::onRecv); sender.testEmit(); // 触发打印方式 2Lambda表达式无接收对象临时逻辑connect(sender, MyObj::sendMsg, this, [](QString s){ qDebug() lambda接收 s;});方式 3Qt4字符串宏运行时检查易写错不推荐connect(sender, SIGNAL(sendMsg(QString)), recv, SLOT(onRecv(QString)));方式 4QMetaObject::connectSlotsByName 自动绑定规则槽函数名 on_对象名_信号名UI控件常用// 控件 objectNamebtn_okclicked 信号 void on_btn_ok_clicked();// 调用一次自动绑定所有匹配槽 QMetaObject::connectSlotsByName(this);4. connect 第五参数连接类型跨线程必用connect(sender, signal, receiver, slot, Qt::ConnectionType);枚举适用场景Qt::AutoConnection默认同线程直连跨线程队列Qt::DirectConnection直连信号发射线程直接执行槽跨线程慎用线程不安全Qt::QueuedConnection队列信号放入接收者线程事件循环跨线程标准用法Qt::BlockingQueuedConnection阻塞队列发射线程阻塞直到槽执行完成禁止同线程死锁Qt::UniqueConnection防止重复连接搭配或使用5. 断开连接 disconnect// 断开指定信号槽 disconnect(sender, MyObj::sendMsg, recv, Receiver::onRecv); // 断开sender所有连接 disconnect(sender, nullptr, nullptr, nullptr); // 断开接收者所有连接 sender.disconnect(recv);五、元对象系统MetaObject反射能力1. 获取元对象QObject *obj new MyObj; const QMetaObject *meta obj-metaObject(); // 静态获取无需实例 const QMetaObject *metaStatic MyObj::staticMetaObject;2. 运行时反射常用API// 获取类名 qDebug() meta-className(); // 动态调用函数函数需 Q_INVOKABLE / slot QString res; meta-invokeMethod(obj, getInfo, Qt::DirectConnection, Q_RETURN_ARG(QString, res)); // 获取属性数量、读写属性 int propCnt meta-propertyCount(); QMetaProperty prop meta-property(0); prop.read(obj); prop.write(obj, newVal);3. Q_PROPERTY动态属性示例class Person : public QObject{ Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)public: QString name() const { return m_name; } void setName(const QString n) { if(m_name ! n) { m_name n; emit nameChanged(m_name); } } signals: void nameChanged(const QString );private: QString m_name;};使用Person p; p.setProperty(name, 张三); // 动态写 qDebug() p.property(name).toString(); // 动态读4. 动态属性无Q_PROPERTY也能用无需提前声明任意QObject运行时附加键值obj-setProperty(tag, 100); int tag obj-property(tag).toInt(); // 判断是否存在属性 obj-dynamicPropertyNames();六、事件处理机制QObject事件循环所有 QObject 都能接收事件依靠 event()、事件过滤器。1. 重写事件处理函数class MyObj : public QObject{ Q_OBJECTprotected: // 总事件分发入口 bool event(QEvent *event) override { if(event-type() QEvent::Timer) { qDebug() 定时器事件; return true; // 消费事件不再传递 } return QObject::event(event); // 交给基类默认处理 }};2. 事件过滤器 installEventFilterA 对象监听 B 对象所有事件拦截处理// 监听者this安装过滤器到 targetObj targetObj-installEventFilter(this); // 重写过滤函数 bool eventFilter(QObject *watched, QEvent *event) override{ if(watched targetObj event-type() QEvent::MouseButtonPress) { return true; // 拦截鼠标点击目标收不到 } return false; // 放行事件 }3. 发送事件同步QCoreApplication::sendEvent(obj, event) 阻塞执行异步QCoreApplication::postEvent(obj, new QEvent(...)) 放入事件队列七、QObject多线程规则1. 对象归属线程每个 QObject 绑定创建时所在线程API// 返回对象依附的线程 QThread *thread() const; // 移动对象到新线程必须无父对象 bool moveToThread(QThread *targetThread);2. 强制铁则1moveToThread 前必须 setParent (nullptr)有父对象移动失败2不能跨线程父子对象父和子必须同线程3不要在子线程直接操作 UI 控件UI 只能主线程4跨线程通信信号槽必须用 Qt::QueuedConnection标准多线程模板QThread *th new QThread; Worker *work new Worker; work-moveToThread(th); // 移至子线程 // 线程启动执行任务 connect(th, QThread::started, work, Worker::doWork); // 任务完成销毁线程 connect(work, Worker::finished, th, QThread::quit);connect(th, QThread::finished, th, QThread::deleteLater);connect(th, QThread::finished, work, Worker::deleteLater); th-start();3. deleteLater 延迟删除多线程安全销毁不会立即释放等待当前事件循环完成再删除obj-deleteLater();推荐替代直接 delete obj避免事件派发中崩溃。八、对象名称objectName// 设置/获取对象名void setObjectName(const QString name); QString objectName() const;用途findChild 按名称查找对象QSS 样式选择器 #objectName日志、调试区分多个同类对象九、禁用拷贝QObject无法复制QObject 禁用拷贝构造、赋值运算符Q_DISABLE_COPY(MyObj) // 内部自动加无需手动写错误写法MyObj a; MyObj b a; // 编译报错禁止拷贝原因父子内存管理、信号槽绑定、线程归属无法复制需要多实例只能 new 创建。十、常用全局配套工具类QCoreApplication管理事件循环所有QObjec 事件分发入口QMetaType自定义类型注册信号槽传递自定义结构体需qRegisterMetaTypeT()QVariant配合 Q_PROPERTY 存储任意类型属性十一、常见报错与踩坑总结undefined reference to vtable类有Q_OBJECT但未moc或忘记执行 qmake/CMake跨线程崩溃父子跨线程、DirectConnection 跨线程直接调用 UI、未用 QueuedConnection重复析构崩溃栈对象作为父子对象手动 delete或 moveToThread 时有父对象信号槽不触发参数类型不匹配、connect 第五参数连接类型错误、对象提前销毁invokeMethod 调用失败函数未加 slot / Q_INVOKABLE参数传参不匹配十二、最简完整可运行示例#include QCoreApplication#include QObject#include QDebug class Sender : public QObject{ Q_OBJECT signals: void msg(const QString s); public: void send() { emit msg(测试信号); } }; class Recv : public QObject { Q_OBJECT public slots: void onMsg(QString s) { qDebug() 接收 s; } }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); Sender s; Recv r; QObject::connect(s, Sender::msg, r, Recv::onMsg); s.send(); return a.exec(); } #include main.moc // 单文件 moc 必需