| | |
| | | #include "errorexample.h" |
| | | |
| | | #include <QFile> |
| | | #include <QLabel> |
| | | #include <QPushButton> |
| | | errorexample::errorexample() { |
| | | // 1.未包含必要的头文件 |
| | | // 错误:缺少 #include <QLabel> |
| | | QLabel label("Hello"); // 编译错误:'QLabel' was not declared in this scope |
| | | label.show(); |
| | | |
| | | // 2.未初始化指针 |
| | | QLabel *label4; |
| | | label4->setText("Hello"); // 错误:使用未初始化的指针 |
| | | //-----------指针常见问题--------------- |
| | | |
| | | // 修复:初始化指针 |
| | | QLabel *label5 = new QLabel; |
| | | //①空指针解引用 |
| | | QWidget *widget = nullptr; |
| | | widget->show(); //对值为nullptr的指针进行访问操作,会引发崩溃 |
| | | |
| | | // 3.父对象管理不当导致内存泄露 |
| | | QLabel *label1 = new QLabel("Memory Leak"); |
| | | label1->show(); |
| | | //解决办法: |
| | | QWidget *widget1 = nullptr; |
| | | if (widget1) { //使用指针前,先确认其是否为nullptr |
| | | widget1->show(); |
| | | } |
| | | |
| | | // 正确做法:设置父对象,Qt会自动管理内存 |
| | | //②野指针 |
| | | //指针所指向的内存已经被释放,但后续仍在使用该指针 |
| | | QWidget *widget2 = new QWidget; |
| | | delete widget2; |
| | | widget2->show(); //此时widget2已经变成野指针 |
| | | |
| | | //解决办法: |
| | | //对象释放之后,及时将指针置为nullptr(利用 Qt |
| | | //对象的父子关系机制来自动管理内存) |
| | | QWidget *widget3 = new QWidget; |
| | | delete widget3; |
| | | widget3 = nullptr; //避免野指针 |
| | | |
| | | //③内存泄露:未释放动态分配的内存 |
| | | void createLeak() { |
| | | int *ptr = new int(10); //分配后未释放 |
| | | } |
| | | //解决办法: |
| | | void createLeak() { |
| | | int *ptr = new int(10); //分配后未释放 |
| | | delete ptr; |
| | | ptr = nullptr; |
| | | } |
| | | |
| | | //④父子关系误用 |
| | | QWidget *parent = new QWidget; |
| | | QLabel *managedLabel = new QLabel("Managed", parent); |
| | | QPushButton *button = new QPushButton(parent); |
| | | delete button; // 手动删除子对象 |
| | | delete parent; // 崩溃!父对象试图再次删除已释放的 button |
| | | |
| | | // 4.使用已删除的对象 |
| | | QLabel *label2 = new QLabel("Delete Me"); |
| | | delete label2; |
| | | //解决办法:依赖对象树自动管理 |
| | | QWidget *parent2 = new QWidget; |
| | | QPushButton *button2 = new QPushButton(parent); |
| | | delete parent2; // 自动删除所有子对象(包括 button) |
| | | |
| | | // 错误:访问已删除的对象 |
| | | label2->setText("Oops"); // 未定义行为 |
| | | //⑤智能指针与对象树冲突 |
| | | //混合使用父对象和智能指针 |
| | | QWidget *parent3 = new QWidget; |
| | | // 错误:父对象已管理 button,智能指针会导致双重释放 |
| | | std::unique_ptr<QPushButton> button3(new QPushButton(parent)); |
| | | |
| | | // 5.字符串编码问题 |
| | | QLabel *label3 = new QLabel; |
| | | // 错误:硬编码非ASCII字符串 |
| | | label3->setText("中文文本"); // 可能显示乱码,取决于源文件编码 |
| | | //解决办法1:仅用父对象管理 |
| | | QWidget *parent4 = new QWidget; |
| | | QPushButton *button4 = new QPushButton(parent4); // 父对象负责释放 |
| | | |
| | | // 正确做法:使用tr()进行翻译 |
| | | label3->setText(tr("中文文本")); |
| | | //解决办法2:无父对象时使用智能指针 |
| | | std::unique_ptr<QPushButton> button5 = std::make_unique<QPushButton>(); |
| | | |
| | | // 6.容器越界访问 |
| | | QList<int> list = {1, 2, 3}; |
| | | |
| | | // 错误:越界访问 |
| | | int value = list[3]; // 索引最大为2 |
| | | |
| | | // 正确做法:使用at()并检查边界 |
| | | if (list.size() > 3) { |
| | | int safeValue = list.at(3); // at()会在越界时抛出异常 |
| | | //⑥容器中的指针管理 |
| | | // 忘记释放files数组及其中的指针所指向的内存 |
| | | QFile *files = new QFile[10]; |
| | | for (int i = 0; i < 10; ++i) { |
| | | files[i].setFileName("file" + QString::number(i)); |
| | | } |
| | | |
| | | // 7.缺失终止条件的For循环 |
| | | for (int i = 0;; i++) { // 错误:无终止条件 |
| | | //循环体 |
| | | //解决办法: |
| | | QFile **files1 = new QFile *[10]; |
| | | for (int i = 0; i < 10; ++i) { |
| | | files[i].setFileName("file" + QString::number(i)); |
| | | } |
| | | for (int i = 0; i < 10; ++i) { |
| | | delete files1[i]; |
| | | } |
| | | delete[] files; |
| | | |
| | | //-----------常见逻辑错误--------------- |
| | | |
| | | //①无限循环 |
| | | //错误:(无终止条件) |
| | | for (int i = 0; i < 10;) { |
| | | qDebug() << "Looping..."; |
| | | // 忘记写i++ |
| | | } |
| | | |
| | | // 正确写法: |
| | | for (int i = 0; i < 10; i++) { // 添加终止条件 |
| | | //循环体 |
| | | //错误:死循环(条件恒为真) |
| | | while (true) { |
| | | //代码块 未提供退出机制 |
| | | } |
| | | |
| | | // 8.误用 = 代替 == |
| | | if (x = 5) { // 误将赋值操作当作比较,条件始终为 true(除非 x 是布尔类型) |
| | | // ... |
| | | //解决办法 |
| | | // 增加i++ |
| | | for (int i = 0; i < 10; i++) { |
| | | qDebug() << "Looping..." << i; |
| | | } |
| | | |
| | | // 9.事件处理未调用基类实现 |
| | | void CustomWidget::paintEvent(QPaintEvent * event) { |
| | | // 错误:未调用基类实现 |
| | | QPainter painter(this); |
| | | // 缺少:QWidget::paintEvent(event); |
| | | //使用break或return退出循环 |
| | | while (true) { |
| | | if (data.isEmpty()) break; // 条件满足时退出 |
| | | } |
| | | |
| | | // 10.信号槽参数不匹配 |
| | | // 错误:信号参数与槽参数类型不匹配 |
| | | connect(sender, &Sender::valueChanged(int), receiver, |
| | | &Receiver::updateValue(QString)); |
| | | //②数据/容器越界访问 |
| | | // 错误:访问越界(索引0~9,但循环到i=10) |
| | | QVector<int> numbers(10); |
| | | for (int i = 0; i <= 10; ++i) { // ӦΪi < 10 |
| | | numbers[i] = i; // i=10时越界 |
| | | // 错误:使用空容器 |
| | | QStringList names; |
| | | qDebug() << names[0]; // 访问空容器的第一个元素 |
| | | |
| | | // 修复:确保参数类型一致 |
| | | connect(sender, &Sender::valueChanged(int), receiver, |
| | | &Receiver::updateValue(int)); |
| | | //解决办法 |
| | | //使用安全的访问方法(如at()代替[]) |
| | | qDebug() << numbers.at(10); |
| | | //检查容器是否为空 |
| | | if (!names.isEmpty()) { |
| | | qDebug() << names[0]; |
| | | } |
| | | |
| | | // 11.未实现纯虚函数 |
| | | class MyInterface { |
| | | public: |
| | | virtual void pureVirtual() = 0; |
| | | }; |
| | | // 2.未初始化指针 |
| | | QLabel *label4; |
| | | label4->setText("Hello"); // 错误:使用未初始化的指针 |
| | | |
| | | class MyClass : public MyInterface { |
| | | // 错误:未实现纯虚函数 |
| | | }; |
| | | // 修复:初始化指针 |
| | | QLabel *label5 = new QLabel; |
| | | |
| | | // 修复:实现纯虚函数 |
| | | void MyClass::pureVirtual() { /* 实现 */ |
| | | // 3.父对象管理不当导致内存泄露 |
| | | QLabel *label1 = new QLabel("Memory Leak"); |
| | | label1->show(); |
| | | |
| | | // 正确做法:设置父对象,Qt会自动管理内存 |
| | | QWidget *parent = new QWidget; |
| | | QLabel *managedLabel = new QLabel("Managed", parent); |
| | | |
| | | // 4.使用已删除的对象 |
| | | QLabel *label2 = new QLabel("Delete Me"); |
| | | delete label2; |
| | | |
| | | // 错误:访问已删除的对象 |
| | | label2->setText("Oops"); // 未定义行为 |
| | | |
| | | // 5.字符串编码问题 |
| | | QLabel *label3 = new QLabel; |
| | | // 错误:硬编码非ASCII字符串 |
| | | label3->setText("中文文本"); // 可能显示乱码,取决于源文件编码 |
| | | |
| | | // 正确做法:使用tr()进行翻译 |
| | | label3->setText(tr("中文文本")); |
| | | |
| | | // 6.容器越界访问 |
| | | QList<int> list = {1, 2, 3}; |
| | | |
| | | // 错误:越界访问 |
| | | int value = list[3]; // 索引最大为2 |
| | | |
| | | // 正确做法:使用at()并检查边界 |
| | | if (list.size() > 3) { |
| | | int safeValue = list.at(3); // at()会在越界时抛出异常 |
| | | } |
| | | |
| | | // 7.缺失终止条件的For循环 |
| | | for (int i = 0;; i++) { // 错误:无终止条件 |
| | | //循环体 |
| | | } |
| | | |
| | | // 正确写法: |
| | | for (int i = 0; i < 10; i++) { // 添加终止条件 |
| | | //循环体 |
| | | } |
| | | |
| | | // 8.误用 = 代替 == |
| | | if (x = 5) { // 误将赋值操作当作比较,条件始终为 true(除非 x 是布尔类型) |
| | | // ... |
| | | } |
| | | |
| | | // 9.事件处理未调用基类实现 |
| | | void CustomWidget::paintEvent(QPaintEvent * event) { |
| | | // 错误:未调用基类实现 |
| | | QPainter painter(this); |
| | | // 缺少:QWidget::paintEvent(event); |
| | | } |
| | | |
| | | // 10.信号槽参数不匹配 |
| | | // 错误:信号参数与槽参数类型不匹配 |
| | | connect(sender, &Sender::valueChanged(int), receiver, |
| | | &Receiver::updateValue(QString)); |
| | | |
| | | // 修复:确保参数类型一致 |
| | | connect(sender, &Sender::valueChanged(int), receiver, |
| | | &Receiver::updateValue(int)); |
| | | |
| | | // 11.未实现纯虚函数 |
| | | class MyInterface { |
| | | public: |
| | | virtual void pureVirtual() = 0; |
| | | }; |
| | | |
| | | class MyClass : public MyInterface { |
| | | // 错误:未实现纯虚函数 |
| | | }; |
| | | |
| | | // 修复:实现纯虚函数 |
| | | void MyClass::pureVirtual() { /* 实现 */ |
| | | } |
| | | |
| | | // 12.未正确实现拷贝构造函数 |
| | | class MyClass1 { |
| | | public: |
| | | QWidget *widget; |
| | | MyClass1(const MyClass1 &other) { |
| | | widget = other.widget; |
| | | } // 错误:浅拷贝 |
| | | }; |
| | | |
| | | // 修复:深拷贝或禁用拷贝构造函数 |
| | | MyClass1(const MyClass1 &other) = delete; |
| | | } |
| | | |
| | | // 12.未正确实现拷贝构造函数 |
| | | class MyClass1 { |
| | | public: |
| | | QWidget *widget; |
| | | MyClass1(const MyClass1 &other) { widget = other.widget; } // 错误:浅拷贝 |
| | | }; |
| | | |
| | | // 修复:深拷贝或禁用拷贝构造函数 |
| | | MyClass1(const MyClass1 &other) = delete; |
| | | } |