2

I have an error, just after closing my application, I get an error message saying that it has stopped unexpectedly. I reached this:

enter image description here

But, after I close the window, I get this error:

The program has unexpectedly finished. The process was ended forcefully.

My project structure is:

enter image description here

And my code is:

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();
}

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

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();
}

login.h

#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

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());
}

Thanks in advance!

Omar Murcia
  • 547
  • 2
  • 11
  • 26
  • 1
    Try to run your program in debug mode and see at what line is crash. – thibsc Dec 27 '17 at 10:30
  • @ThibautB. Yes! You are right! The order in the destructor is the problem. I only delete `central` in the destructor, and it solved my problem. ^_^ thank you so much!. – Omar Murcia Dec 27 '17 at 10:36

2 Answers2

6

When you add widgets to a layout, e.g.

this->layout.addRow(&(this->lblHost), &(this->leHost));

you're parenting them to the layout's widget, in this case your Login main window, and when the parent widget destructor will be called, all children widget will be delete'd. What happens in your code is: the children (line edits and labels) are not instantiated with new, so calling delete on them will crash your application. You should replace the children declarations in your widget header with pointers, this way:

QLabel * lblHost;

// ...

QLineEdit * leHost;

// etc

and instantiate them before adding them to the layout:

this->lblHost = new QLabel();
this->leHost = new QLineEdit();
this->layout.addRow(this->lblHost, this->leHost);
//etc

This applies to all layouts and widgets that have a parent (i.e. central Qwidget).

Also: explicitly calling delete on children is not needed, because the parent destructor will take care of that, and this applies to the central widget as well:

QMainWindow takes ownership of the widget pointer and deletes it at the appropriate time

As thuga points out in comments, there is nothing inherently wrong in instantiating children on the stack, as long as their destructor is called before their parent's, thus they are automatically removed from the parent's children list and the parent destructor will not call delete on them.

As explained here, this is legit:

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

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

    return a.exec();
}

because label will go out of scope before widget.

Changing the two objects creation order will cause the application crash at exit time:

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

    QLabel label;
    QWidget widget;

    label.setParent(&widget);

    widget.show();

    return a.exec();
}
p-a-o-l-o
  • 9,807
  • 2
  • 22
  • 35
  • Thank you, but I get the same error. I updated the question with your advices. – Omar Murcia Dec 27 '17 at 09:37
  • You should make it more clear that OP should *remove* his `delete` calls in the destructor since the widgets parent will delete them. – Jesper Juhl Dec 27 '17 at 09:56
  • @p-a-o-l-o you are right! The destructor is the problem, I only must delete `central`. My problem is solved. Thank you so much!. – Omar Murcia Dec 27 '17 at 10:39
  • 1
    You don't have to delete it at all. Read here: http://doc.qt.io/qt-5/qmainwindow.html#setCentralWidget – p-a-o-l-o Dec 27 '17 at 10:41
  • @p-a-o-l-o I have a doubt, why I must create QLabels and QLineEdits as pointers and not as static objects??? – Omar Murcia Dec 27 '17 at 10:48
  • 2
    It's not that destroying those labels will cause a crash, it's that destroying them twice will cause a crash. Using pointers is not necessary, however you must pay attention to the order of destruction in that case. See [object trees and ownership](http://doc.qt.io/qt-5/objecttrees.html). – thuga Dec 27 '17 at 10:49
  • 2
    Well, the answer is all about that. The parent widget will call delete on them and calling delete on a object that has not been instantiated with new is a recipe for segmentation fault. – p-a-o-l-o Dec 27 '17 at 10:50
  • 2
    That is true. But the point I'm trying to make is that it doesn't matter if you allocate an object with `new` or use automatic variables. What matters is that if you destroy the children yourself, or if you use automatic variables, you have to make sure that the children are destroyed before the parent's destructor is called. Any children that are destroyed are automatically removed from the parent's list of children. – thuga Dec 27 '17 at 12:41
0

You have deleting QMainWindow elements by yourself :

delete this->lblHost;

When your QObject inherited objects are created on heap with parent, you should not delete it by yourselves. because QObject tree will take care of the memory memory management.

So twice deleting would be happen.