)
前置记住Qt 是C 框架下面这些是 Qt 额外增加的宏 / 关键字不是标准 C靠 moc元对象编译器处理。普通 C 语法if、for、class、指针、vector 等照常使用。 环境Qt Creator Qt Widgets 项目一、Q_OBJECT【最重要90% 新手报错根源】写法#include QObject class MyClass : public QObject // 必须继承QObject或者它的子类QWidget/QMainWindow等 { Q_OBJECT // 这一行 public: explicit MyClass(QObject *parent nullptr); };详细解释作用开启 Qt【元对象系统】。有了它才能使用信号槽、反射、Q_PROPERTY。强制规则新手必背Q_OBJECT必须写在头文件 (.h)的类里面这个类必须继承 QObject窗口类 QMainWindow、QWidget 都是 QObject 子类类名后面大括号内第一行附近写⚠️ 踩坑加了 Q_OBJECT 之后一定要 qmake/CMake 重新构建不然会报undefined reference to vtable虚表错误。简单一句话要用信号槽就必须写 Q_OBJECT不用信号槽可以不写。二、signals:信号写法signals: void sendData(int num); // 信号函数声明详细解释信号类对外发出的通知代表 “发生了某个事件”。 例按钮点击clicked()就是 QPushButton 自带的信号。特点只写声明不用写函数实现不需要写 {} 函数体moc 自动生成代码信号可以带参数用来传递数据信号函数返回值一般是void触发信号用emit。理解类比信号 报警器出事了就响只负责通知不做处理。三、slots:槽函数旧写法了解即可Qt5 之后推荐用普通成员函数当槽slots:属于老式语法public slots: void onRecvData(int num);槽函数用来接收信号、执行具体业务逻辑的函数。特点必须自己写函数实现.cpp 里写函数体参数数量、类型最好和发送的信号匹配。类比槽 接到报警器通知后执行的动作关门、报警提示✅ Qt5 新写法推荐不用写slots:普通 public 函数就可以作为槽直接 connect 连接。四、emit发射信号emit sendData(100);解释emit是一个空宏编译后直接消失只是给人看标记作用触发信号。当代码执行emit xxx()就会把这个信号发送出去所有连接到这个信号的槽函数会被调用。只能在信号所在的类的成员函数内部emit。一句话emit 按下报警器开关。信号槽完整最小示例h 文件#ifndef MYCLASS_H #define MYCLASS_H #include QObject class MyClass : public QObject { Q_OBJECT public: explicit MyClass(QObject *parent nullptr); void testEmit(); // 普通函数用来发射信号 signals: void sendMsg(QString str); // 信号不带实现 public: void recvMsg(QString str); // Qt5新方式普通函数直接做槽 }; #endif // MYCLASS_Hcpp 文件#include myclass.h #include QDebug MyClass::MyClass(QObject *parent) : QObject(parent) { } void MyClass::testEmit() { emit sendMsg(Hello Qt); // 发射信号 } void MyClass::recvMsg(QString str) { qDebug() 收到消息 str; }五、connect()信号槽连接函数【最常用】Qt5 新式写法推荐新手优先学这个类型安全connect(objA, MyClass::sendMsg, objB, MyClass::recvMsg);参数拆解connect(发送者对象, 类名::信号名, 接收者对象, 类名::槽函数)objA信号是谁发出来的对象MyClass::sendMsg取信号函数的地址objB谁接收信号MyClass::recvMsg接收后执行的函数地址旧版 Qt4 字符串写法不推荐容易写错类型不检查// Qt4老写法尽量不要用 connect(objA, SIGNAL(sendMsg(QString)), objB, SLOT(recvMsg(QString)));connect 第五个可选参数连接方式了解connect(a, A::sig, b, B::slot, Qt::QueuedConnection);Qt::AutoConnection默认自动判断同线程 / 跨线程Qt::DirectConnection同步发射信号立刻执行槽同线程Qt::QueuedConnection异步放进接收者事件队列跨线程必须用这个六、explicit构造函数类构造函数前面explicit MyClass(QObject *parent nullptr);作用禁止隐式类型转换。Qt 所有继承 QObject 的类构造基本都会加 explicit。新手记住写继承 QObject 的类构造函数前面加上explicit就对了。parent nullptr父对象Qt 内存管理核心。七、parent 父对象机制Qt 内存管理非常重要// new一个对象指定父对象为this QPushButton *btn new QPushButton(按钮, this);普通 Cnew 出来的对象必须手动 delete否则内存泄漏Qt QObject 子类指定 parent 父对象父对象销毁时自动 delete 所有子对象不用手动 deletethis代表当前类实例。注意只对继承 QObject 的类生效普通 C 类没用。八、QDebug打印输出代替 cout#include QDebug qDebug() 数字 123 字符串 QString(测试);qDebug()Qt 自带打印专门输出 Qt 类型QString、QVariant可以连续拼接不需要 endl自动换行发布版本可以关掉 qDebug 输出。九、QStringQt 字符串高频使用标准 C 是std::stringQt 界面用QStringQString str hello; // 互转 std::string s str.toStdString(); QString str2 QString::fromStdString(s);常用方法str.isEmpty(); // 判断是否空 str.size(); // 字符长度 str.arg(100); // 字符串占位替换超级常用 QString info QString(当前数值%1).arg(99); // info结果当前数值99十、this关键字和 C 一样Qt 大量使用this指向当前实例对象。 常见场景构造函数初始化列表connect 里传递当前对象作为发送 / 接收者new 控件时设置 parent this十一、初始化列表C 语法但 Qt 构造函数天天用MyClass::MyClass(QObject *parent) : QObject(parent) // 初始化列表调用父类构造函数 { }: QObject(parent)子类构造时先调用父类 QObject 的构造把 parent 传给父对象。所有继承 QObject 的类构造函数都要写这个。二、新手高频踩坑汇总加了Q_OBJECT后忘记qmake 重新构建报虚表 undefined reference。解决【构建】-【qmake】再重新构建项目信号参数多于槽函数参数可以槽参数多于信号参数不行QObject 的子类不能拷贝、不能赋值不能写MyClass a b;QObject 禁用拷贝构造。界面控件QPushButton、QLabel操作必须在主线程子线程不能直接操作 UI 控件。三、最简完整可运行 Demo控制台 Qt 项目myclass.h#ifndef MYCLASS_H #define MYCLASS_H #include QObject class MyClass : public QObject { Q_OBJECT public: explicit MyClass(QObject *parent nullptr); void test(); signals: void sendText(QString txt); public: void slotRecv(QString txt); }; #endif // MYCLASS_Hmyclass.cpp#include myclass.h #include QDebug MyClass::MyClass(QObject *parent) : QObject(parent) { } void MyClass::test() { emit sendText(Qt信号槽测试); } void MyClass::slotRecv(QString txt) { qDebug() 收到信号内容 txt; }main.cpp#include QCoreApplication #include myclass.h int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); MyClass obj1; MyClass obj2; // 连接信号槽 connect(obj1, MyClass::sendText, obj2, MyClass::slotRecv); obj1.test(); // 调用函数发射信号 return a.exec(); }运行输出收到信号内容Qt信号槽测试