一句话:Qt 类本质就是 C++ 的类,只是 Qt 框架扩展了一套【QObject 对象模型】。 普通 C++ 类:只封装变量、成员函数;继承 QObject 的 Qt 类:除了普通 C++ 能力,额外拥有【信号槽、对象树自动内存管理、元对象系统】三大核心能力Qt
一、最核心根类:QObject(重中之重)
QObject = Qt 世界所有对象的祖宗基类几乎所有控件、窗口、定时器、线程类,都是它的子类(直接继承 / 间接继承)。
注意区分两个概念:
- 继承关系(父类 / 子类):C++ 的类继承,QWidget 继承 QObject;
- 对象树父子 (parent):对象实例之间的归属,用来自动管理内存,不是 C++ 继承!
✅ QObject 带来 3 个核心功能:
- 信号与槽(对象通信):对象之间不用硬调用函数,通过信号槽收发消息(按钮点击就是典型信号)
- 对象树自动内存管理(parent 机制)new 对象时传入
this作为父对象;父对象销毁,自动删除全部子对象,不用手动 delete,减少内存泄漏 - 元对象系统(moc 工具):运行时知道类名字、函数,实现 Q_OBJECT 宏的能力
⚠️ 例外:
QString、QList、QColor这类值类型类,不继承 QObject,和普通 C++ 的 string、vector 用法接近,不能写 Q_OBJECT、不能用信号槽。
二、可视化窗口核心类:QWidget(所有界面控件的基类)
QWidget继承自QObject + QPaintDevice(绘图设备)只要能在屏幕上显示出来的东西,全部是 QWidget 子类:
- QMainWindow:主窗口(带菜单栏、状态栏,Qt 新建项目默认的窗口类)
- QDialog:弹窗对话框(提示框、文件选择框)
- QPushButton:按钮
- QLabel:文本 / 图片标签
- QLineEdit:单行输入框
简单理解:QObject 负责逻辑通信、内存管理;QWidget 额外增加界面绘制、鼠标键盘事件。
三、写一个自己的 Qt 类(完整示例,逐行解释)
新建头文件myclass.h
#ifndef MYCLASS_H #define MYCLASS_H #include <QObject> // 自定义Qt类:继承QObject class MyClass : public QObject { // 开启元对象系统,必须写,才能用信号槽 Q_OBJECT public: // 构造函数,explicit防止隐式转换;parent父对象默认nullptr explicit MyClass(QObject *parent = nullptr); // 普通成员函数,用来发射信号 void testSend(); // 信号:对外发出通知,只声明,不写函数体 signals: void msgSignal(QString text); // 槽函数:接收信号,执行业务 public: void recvSlot(QString text); }; #endif // MYCLASS_Hcpp 文件myclass.cpp
#include "myclass.h" #include <QDebug> // 构造函数:初始化列表调用父类QObject构造,传入parent MyClass::MyClass(QObject *parent) : QObject(parent) { } void MyClass::testSend() { // emit发射信号 emit msgSignal("Hello Qt类!"); } // 槽函数实现,收到信号后打印 void MyClass::recvSlot(QString text) { qDebug() << "槽收到消息:" << text; }main.cpp
#include <QCoreApplication> #include "myclass.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); MyClass obj1; // 信号发送对象 MyClass obj2; // 接收对象 // connect连接信号槽 connect(&obj1, &MyClass::msgSignal, &obj2, &MyClass::recvSlot); obj1.testSend(); // 调用函数,触发emit发射信号 return a.exec(); }运行输出:槽收到消息:"Hello Qt类!"
四、对象树(parent 机制,新手最容易混淆)
// 创建按钮,父对象是当前窗口this QPushButton *btn = new QPushButton("测试按钮", this);图解逻辑:
- btn 的 parent = this(当前窗口对象)
- 窗口对象会把 btn 加入自己的 children 子对象列表
- 关闭窗口,窗口对象销毁,自动把 btn delete,不用手动释放内存
❗重点:parent 是对象实例之间的归属关系,不是类的继承! 类比: 类的继承:人 → 学生(模板的继承) 对象树 parent:家长 → 孩子(实例的归属,家长消失孩子跟着销毁)
五、Qt 类 和 普通 C++ 类对比表
| 对比项 | 普通 C++ 类 | 继承 QObject 的 Qt 类 |
|---|---|---|
| 继承 | 默认无父类 | 必须继承 QObject/QWidget 等 |
| Q_OBJECT | 不能写 | 需要写,开启元对象系统 |
| 信号槽 | 不支持 | 支持,emit+connect |
| 内存管理 | new 之后必须手动 delete | 设置 parent,父对象自动回收子对象 |
| 拷贝赋值 | 支持 | ❌禁止拷贝,不能objA=objB |
| 运行时识别 | RTTI 才可以 | 自带元对象,直接获取类名 |
六、新手高频踩坑
- 写 Q_OBJECT,忘记执行 qmake,编译报
undefined reference to vtable虚表错误 解决:菜单栏【构建】→【Run qmake】再重新构建项目 - QObject 子类不能拷贝,不能写
MyClass a = b;,只能用指针 - UI 控件(QPushButton、QLabel)属于 QWidget,只能在主线程操作,子线程不能直接修改界面
QString/QList这些值类型不是 QObject 子类,不能写 Q_OBJECT,没有 parent,用法和 std::string 类似