在Qt中关闭QMainWindow后如何防止崩溃?

Oma*_*cia 2 c++ crash qt qmainwindow

我有一个错误,在关闭我的应用程序后,我收到一条错误消息,提示它已意外停止。我达到了:

在此处输入图片说明

但是,关闭窗口后,出现此错误:

该程序意外完成。该过程被迫结束。

我的项目结构是:

在此处输入图片说明

我的代码是:

main.cpp

#include "mainwindow.h"
#include <QApplication>
#include <windows/login.h>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Login w;
    w.show();

    return a.exec();
}
Run Code Online (Sandbox Code Playgroud)

util_window.h

#ifndef UTIL_H
#define UTIL_H

#include <QObject>
#include <QMainWindow>

class UtilWindow : QObject
{
public:
    // vars

    // methods
    util();
    void setCenterWindow(QMainWindow* w);
};

#endif // UTIL_H
Run Code Online (Sandbox Code Playgroud)

util_window.cpp

#include "util_window.h"
#include <QDesktopWidget>

UtilWindow::util()
{

}

void UtilWindow::setCenterWindow(QMainWindow *w) {
    int width = w->frameGeometry().width();
    int height = w->frameGeometry().height();
    QDesktopWidget wid;
    int screenWidth = wid.screen()->width();
    int screenHeight = wid.screen()->height();
    w->setGeometry((screenWidth/2)-(width/2),(screenHeight/2)-(height/2),width,height);
    w->show();
}
Run Code Online (Sandbox Code Playgroud)

登录名

#ifndef LOGIN_H
#define LOGIN_H

#include <QMainWindow>
#include <QObject>
#include <QWidget>
#include "util/util_window.h"
#include <QGroupBox>
#include <QLabel>
#include <QLineEdit>
#include <QFormLayout>

class Login : public QMainWindow
{
    Q_OBJECT
public:
    // vars
    UtilWindow* util;
    // widgets
    // labels
    QLabel* lblHost;
    QLabel* lblPort;
    QLabel* lblUser;
    QLabel* lblPass;
    // textbox
    QLineEdit* leHost;
    QLineEdit* lePass;
    QLineEdit* leUser;
    QLineEdit* lePort;
    // layouts
    QFormLayout* layout;
    QWidget* central;
    QVBoxLayout* mainLayout;
    QGroupBox* gbCredentials;
    // methods
    void initLabels();
    void initTextBoxes();
    void initUI();
    explicit Login(QWidget *parent = nullptr);
    ~Login();

signals:

public slots:
};

#endif // LOGIN_H
Run Code Online (Sandbox Code Playgroud)

login.cpp

#include "login.h"

Login::Login(QWidget *parent) : QMainWindow(parent)
{
    this->util = new UtilWindow();
    this->initUI();

}

Login::~Login() {
    delete this->lblHost;
    delete this->lblPass;
    delete this->lblPort;
    delete this->lblUser;
    delete this->leHost;
    delete this->lePass;
    delete this->lePort;
    delete this->leUser;
    delete this->layout;
    delete this->central;
    delete this->mainLayout;
    delete this->gbCredentials;
    delete this->util;
}

void Login::initUI() {
    this->setFixedSize(400,400);
    this->setWindowTitle(tr("Inicio de sesión"));
    this->util->setCenterWindow(this);
    this->initLabels();
    this->initTextBoxes();
    this->layout = new QFormLayout();
    this->layout->addRow(this->lblHost, this->leHost);
    this->layout->addRow(this->lblPort, this->lePort);
    this->layout->addRow(this->lblUser, this->leUser);
    this->layout->addRow(this->lblPass, this->lePass);
    this->gbCredentials = new QGroupBox();
    this->gbCredentials->setTitle(tr("Datos de conexión"));
    this->gbCredentials->setLayout(layout);
    this->mainLayout = new QVBoxLayout();
    this->mainLayout->addWidget(gbCredentials);
    this->central = new QWidget();
    this->central->setParent(this);
    this->central->setLayout(this->mainLayout);
    this->setCentralWidget(this->central);
}

void Login::initLabels() {
    this->lblHost = new QLabel();
    this->lblPass = new QLabel();
    this->lblPort = new QLabel();
    this->lblUser = new QLabel();
    this->lblHost->setText(tr("Host: "));
    this->lblPass->setText(tr("Contraseña: "));
    this->lblPort->setText(tr("Puerto: "));
    this->lblUser->setText(tr("Usuario: "));
}

void Login::initTextBoxes() {
    this->leHost = new QLineEdit();
    this->lePass = new QLineEdit();
    this->lePort = new QLineEdit();
    this->leUser = new QLineEdit();
    this->leHost->setPlaceholderText(tr("Host de MySQL"));
    this->lePass->setPlaceholderText(tr("Ingrese su contraseña"));
    this->leUser->setPlaceholderText(tr("Ingrese su nombre de usuario"));
    this->lePort->setPlaceholderText(tr("Ingrese el número de puerto"));
    this->leHost->setToolTip(this->leHost->placeholderText());
    this->leUser->setToolTip(this->leUser->placeholderText());
    this->lePass->setToolTip(this->lePass->placeholderText());
    this->lePort->setToolTip(this->lePort->placeholderText());
}
Run Code Online (Sandbox Code Playgroud)

提前致谢!

p-a*_*l-o 5

将小部件添加到布局时,例如

this->layout.addRow(&(this->lblHost), &(this->leHost));
Run Code Online (Sandbox Code Playgroud)

您将它们作为布局小部件的父级,在本例中为Login主窗口,当父级小部件析构函数被调用时,所有子级小部件都将为delete'd。您的代码中发生的是:子项(行编辑和标签)未使用实例化new,因此对它们调用delete将使您的应用程序崩溃。您应使用指针将小部件标题中的子级声明替换为指针:

QLabel * lblHost;

// ...

QLineEdit * leHost;

// etc
Run Code Online (Sandbox Code Playgroud)

并实例化它们,然后再将它们添加到布局中:

this->lblHost = new QLabel();
this->leHost = new QLineEdit();
this->layout.addRow(this->lblHost, this->leHost);
//etc
Run Code Online (Sandbox Code Playgroud)

这适用于具有父级(即中央Qwidget)的所有布局和小部件。

另外:delete不需要显式调用子级,因为父级析构函数将解决这个问题,这也适用于中央小部件

QMainWindow获取窗口小部件指针的所有权,并在适当的时间删除它

正如thuga在评论中指出的那样,实例化堆栈上的子代并没有本质上的错误,只要在其父代的子代之前先调用它们的析构函数即可,因此它们会自动从父代的子代列表中删除,并且父代的析构函数不会对其进行调用delete

正如解释在这里,这是合法的:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QWidget widget;
    QLabel label(&widget);
    widget.show();

    return a.exec();
}
Run Code Online (Sandbox Code Playgroud)

因为之前label会超出范围widget

更改两个对象的创建顺序将导致应用程序在退出时崩溃:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QLabel label;
    QWidget widget;

    label.setParent(&widget);

    widget.show();

    return a.exec();
}
Run Code Online (Sandbox Code Playgroud)

  • 不是销毁这些标签会导致崩溃,而是销毁它们两次会导致崩溃。不必使用指针,但是在这种情况下,您必须注意破坏的顺序。请参阅[对象树和所有权](http://doc.qt.io/qt-5/objecttrees.html)。 (2认同)
  • 好吧,答案就此而已。父窗口小部件将对它们调用delete,而对尚未用new实例化的对象调用delete是分割错误的秘诀。 (2认同)
  • 那是真实的。但是我要说明的一点是,无论您分配带有`new`的对象还是使用自动变量都没有关系。重要的是,如果您自己销毁了子项,或者使用自动变量,则必须确保在调用父项的析构函数之前销毁了子项。任何被销毁的孩子都会自动从父母的孩子列表中删除。 (2认同)