C 基础回顾Qt 实战版Qt 是 C 框架写 Qt 之前必须把 C 核心概念过一遍。本章只讲 Qt 开发中天天用到的部分每个知识点都配 Qt 场景示例。一、类与对象1.1 基本语法#includeQString/** * brief 用户类演示封装、构造函数、成员函数 */classUser{public:/** * brief 构造函数 * param name 用户名 * param age 年龄 */User(constQStringname,intage):m_name(name),m_age(age)// 初始化列表比赋值更高效{}/** * brief 获取用户名const 成员函数不修改对象状态 * return 用户名 */QStringname()const{returnm_name;}/** * brief 设置用户名 * param name 新用户名 */voidsetName(constQStringname){m_namename;}/** * brief 获取年龄 * return 年龄 */intage()const{returnm_age;}private:QString m_name;/// 成员变量加 m_ 前缀是 Qt 惯例intm_age;};1.2 Qt 中的类Qt 里几乎所有东西都是类控件是类、窗口是类、甚至字符串QString也是类。写 Qt 就是继承 Qt 的类重写虚函数连接信号槽。二、继承与多态2.1 继承#includeQWidget#includeQPainter/** * brief 自定义圆形控件继承 QWidget * * Qt 中自定义控件的标准做法继承 QWidget重写 paintEvent。 */classCircleWidget:publicQWidget{Q_OBJECT// 必须加这个宏才能用信号槽、tr() 等特性public:/** * brief 构造函数 * param parent 父对象传入后内存由父对象管理 */explicitCircleWidget(QWidget*parentnullptr):QWidget(parent){setMinimumSize(100,100);}protected:/** * brief 重写绘制事件虚函数多态 * param event 绘制事件对象 * * 系统需要重绘控件时自动调用我们只负责画什么。 */voidpaintEvent(QPaintEvent*event)override{Q_UNUSED(event);// 标记参数未使用避免编译器警告QPainterpainter(this);painter.setRenderHint(QPainter::Antialiasing);painter.setBrush(Qt::blue);painter.drawEllipse(rect().adjusted(5,5,-5,-5));}};2.2 多态关键点override关键字显式标记重写虚函数编译器会检查签名是否匹配。virtual析构函数基类析构函数必须是虚函数否则通过基类指针删除派生类对象会内存泄漏。Qt 的QObject析构函数已经是虚的。纯虚函数virtual void func() 0;包含纯虚函数的类是抽象类不能实例化。三、指针与引用3.1 指针Qt 中控件几乎都用指针操作因为控件对象通常在堆上创建生命周期由对象树管理。多态必须通过指针或引用实现。#includeQPushButton/** * brief 演示 Qt 控件指针的创建与父子关系 */voiddemoPointer(){// 在堆上创建按钮父对象是 window// window 销毁时会自动 delete 这个按钮不需要手动释放QPushButton*btnnewQPushButton(确定,window);// 用 - 访问成员btn-setText(OK);btn-show();// 空指针检查if(btn!nullptr){btn-setEnabled(true);}}3.2 引用引用常用于函数参数避免拷贝比指针更安全不能为空。#includeQString#includeQStringList/** * brief 拼接字符串列表 * param list 字符串列表const 引用避免拷贝且不修改 * param sep 分隔符 * return 拼接后的字符串 */QStringjoinList(constQStringListlist,constQStringsep){QString result;for(constQStrings:list){// 范围 for 引用高效遍历if(!result.isEmpty())resultsep;results;}returnresult;}Qt 惯例QString、QByteArray等容器类传参用const 控件类传指针。四、STL 容器Qt 有自己的容器类QList、QMap、QStringList等也兼容 STL。两者都要会。4.1 常用容器对比用途STLQt 推荐动态数组std::vectorTQListT字符串列表std::vectorstd::stringQStringList键值对std::mapK,VQMapK,V哈希表std::unordered_mapK,VQHashK,V字符串std::stringQString4.2 QList 实战#includeQList#includeQDebug/** * brief 演示 QList 常用操作 */voiddemoQList(){QListintnumbers;// 增numbers102030;// 流式插入Qt 特色numbers.append(40);numbers.prepend(5);// 头部插入numbers.insert(2,15);// 在索引 2 处插入// 查qDebug()numbers[0];// 10qDebug()numbers.first();// 第一个qDebug()numbers.last();// 最后一个qDebug()numbers.size();// 大小qDebug()numbers.contains(20);// 是否包含// 删numbers.removeAt(0);// 删除索引 0numbers.removeAll(20);// 删除所有值为 20 的元素numbers.clear();// 清空// 遍历QListQStringfruits{apple,banana,cherry};for(constQStringf:fruits){qDebug()f;}// 带索引遍历for(inti0;ifruits.size();i){qDebug()ifruits[i];}}4.3 QMap / QHash 实战#includeQMap#includeQHash#includeQDebug/** * brief 演示 QMap 与 QHash */voiddemoMap(){// QMap有序按 key 排序基于红黑树QMapQString,intscores;scores[Alice]95;scores[Bob]88;scores[Charlie]92;// 遍历按 key 字母序输出for(autoitscores.begin();it!scores.end();it){qDebug()it.key()it.value();}// QHash无序基于哈希表查找更快QHashQString,QStringconfig;config[host]127.0.0.1;config[port]8080;qDebug()config.value(host,localhost);// 带默认值}五、智能指针C 裸指针容易内存泄漏智能指针自动管理生命周期。但Qt 有对象树机制控件通常不需要智能指针。智能指针主要用于非 QObject 的业务对象。5.1 unique_ptr#includememory#includeQDebug/** * brief 业务数据类非 QObject */classConfig{public:Config(){qDebug()Config 构造;}~Config(){qDebug()Config 析构;}voidload(){qDebug()加载配置;}};/** * brief 演示 unique_ptr独占所有权离开作用域自动释放 */voiddemoUniquePtr(){autocfgstd::make_uniqueConfig();// C14 推荐写法cfg-load();// 函数结束时 cfg 自动 delete不会泄漏}5.2 shared_ptr#includememory/** * brief 演示 shared_ptr共享所有权引用计数为 0 时释放 */voiddemoSharedPtr(){autop1std::make_sharedConfig();{autop2p1;// 引用计数 1qDebug()引用计数:p1.use_count();// 2}// p2 销毁引用计数 -1qDebug()引用计数:p1.use_count();// 1}// p1 销毁引用计数 0对象释放5.3 Qt 中的智能指针Qt 也提供了自己的智能指针QScopedPointerT类似unique_ptr独占。QSharedPointerT类似shared_ptr共享。QPointerT专门用于 QObject对象被销毁后自动置空弱引用非常实用。#includeQPointer#includeQPushButton/** * brief 演示 QPointer监控 QObject 生命周期 */voiddemoQPointer(){QPointerQPushButtonbtnnewQPushButton(测试);qDebug()是否有效:!btn.isNull();// truedeletebtn;// 模拟对象被销毁比如父窗口释放了它qDebug()是否有效:!btn.isNull();// false自动置空不会野指针if(btn){// 安全检查btn-setText(安全);}}QPointer 是 Qt 独有的利器当你持有一个控件指针但不确定它是否还活着时用 QPointer 包一下判空即可绝不会野指针。六、Lambda 表达式Lambda 是 C11 引入的匿名函数在 Qt 中写槽函数时极其常用省去单独写成员函数的麻烦。6.1 语法[捕获列表](参数列表)mutable-返回类型{函数体}6.2 Qt 中的典型用法#includeQPushButton#includeQLabel#includeQVBoxLayout/** * brief 演示 Lambda 作为槽函数 * param parent 父窗口 */voidsetupLambdaDemo(QWidget*parent){auto*layoutnewQVBoxLayout(parent);auto*labelnewQLabel(0,parent);auto*btnnewQPushButton(加1,parent);layout-addWidget(label);layout-addWidget(btn);intcount0;// 连接信号到 Lambda// []值捕获所有外部变量count、label 都会被拷贝一份// mutable允许修改值捕获的 countQObject::connect(btn,QPushButton::clicked,[]()mutable{count;label-setText(QString::number(count));});// []引用捕获注意生命周期控件被销毁后引用会悬空// [label]只捕获 label// [this]捕获当前对象指针在类成员函数中常用}6.3 捕获方式总结写法含义使用场景[]不捕获纯逻辑不依赖外部变量[]值捕获所有用到的变量控件指针、简单值最常用[]引用捕获所有用到的变量需要修改外部变量且确保生命周期[this]捕获当前对象指针类成员函数中访问成员变量[, x]默认值捕获x 用引用捕获混合场景Qt 中 Lambda 连接信号的注意事项如果 Lambda 中访问了某个控件确保该控件在信号触发时仍然存在。用[]捕获控件指针是安全的因为指针是值拷贝。七、核心要点总结类与对象Qt 一切皆对象自定义控件继承 QWidget重写虚函数。继承多态override关键字必加Q_OBJECT宏是信号槽的前提。指针引用Qt 控件用指针堆上 对象树管理数据类型用const 传参。容器优先用 Qt 容器QList、QMap、QString与 Qt API 无缝衔接。智能指针非 QObject 业务对象用unique_ptr/shared_ptrQObject 用QPointer防野指针。Lambda写短槽函数的首选[]值捕获最常用注意控件生命周期。初始化列表构造函数中用初始化列表比函数体内赋值更高效是 Qt 惯例。下一章Qt 基础 - 对象树与内存管理