正确的方法来结束QT应用程序。分段错误

huangapple go评论55阅读模式
英文:

Correct way to finish QT app. Segmentation fault

问题

我正在学习Qt和C++,尝试创建一个简单的Qt应用程序。我不明白如何正确销毁类。我需要调用MainWidget的析构函数来停止定时器。但是当我尝试删除MainWidget时,出现"段错误"。有人可以帮忙理解如何正确做这件事吗?

GUI.cpp

#include "GUI.hpp"

Gui::Gui()
{
    led = new Led();
    mainWidget = new MainWidget();
    mainWidget->show();

    connect(this, SIGNAL(sendOneSignal()),
               mainWidget, SLOT(slotFunc()));
}

Gui::~Gui()
{
    delete led;
    //delete mainWidget; //segmentation error occurs
}

void Gui::func(void)
{
    emit sendOneSignal();
}

GUI.hpp

#ifndef __GUI_HEADER__
#define __GUI_HEADER__

#include "Led.hpp"
#include "MainWidget.hpp"

class Gui : public QObject
{
    Q_OBJECT

public:
    Gui();
    ~Gui();
    void func(void);
signals:
     void sendOneSignal(void);

private:
    Led *led;
    MainWidget *mainWidget;

};

typedef std::unique_ptr<Gui> GuiPtr;

#endif /* __GUI_HEADER__ */

MainWidget.cpp

#include "MainWidget.hpp"

MainWidget::MainWidget(QWidget *parent)
: QWidget(parent)
{
    setWindowModality(Qt::ApplicationModal);
    
    timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, &MainWidget::onTimeChanged);
    timer->start(1000);

    cardLabel = new QLabel();
    cardLabel->setAlignment(Qt::AlignRight|Qt::AlignTop);
    cardLabel->setPixmap(QPixmap(":/icons/img"));
}

MainWidget::~MainWidget()
{
    timer->stop();
    delete timer;
    delete cardLabel;
}

void MainWidget::onTimeChanged()
{
    ////
}

void MainWidget::slotFunc()
{
    ///
}

MainWidget.hpp

#ifndef MAINWINDOW_HPP
#define MAINWINDOW_HPP

//#include <QMainWindow>
#include <QTimer>
#include <QWidget>
#include <QFile>
#include <QLabel>
#include <QLayout>

typedef std::unique_ptr<QMessageBox> MessageBoxPtr;

class MainWidget : public QWidget
{
    Q_OBJECT

public:
    explicit MainWidget(QWidget *parent = nullptr);
    ~MainWidget();

protected slots:
    void slotFunc();

private slots:
    void onTimeChanged();

private:
    QLabel *cardLabel;
    QTimer *timer;
    QVBoxLayout *vbox;
    QHBoxLayout *hbox;
    bool isMessageBoxClosed = true;
};

#endif // MAINWINDOW_HPP
英文:

I'm studiyng Qt and C++ and trying to make a simple Qt application. I don't understand how to destroy classes correctly. I need the destructor of MainWidget to be called to stop the timer. But when I try to delete MainWidget I'm getting "segmentation fault". Can somebody help to understand how to do this in right way?

GUI.cpp

#include &quot;GUI.hpp&quot;

Gui::Gui()
{
    led = new Led();
    mainWidget = new MainWidget();
    mainWidget-&gt;show();

    connect(this, SIGNAL(sendOneSignal()),
               mainWidget, SLOT(slotFunc()));
}

Gui::~Gui()
{

    delete led;
    //delete mainWidget; //segmentation error occurs
}

void Gui::func(void)
{
    emit sendOneSignal();
}

GUI.hpp

#ifndef __GUI_HEADER__
#define __GUI_HEADER__

#include &quot;Led.hpp&quot;
#include &quot;MainWidget.hpp&quot;

class Gui : public QObject
{
    Q_OBJECT

public:
    Gui();
    ~Gui();
    void func(void);
signals:
     void sendOneSignal(void);

private:
    Led *led;
    MainWidget *mainWidget;

};

typedef std::unique_ptr&lt;Gui&gt; GuiPtr;

#endif /* __GUI_HEADER__ */

MainWidget.cpp

#include &quot;MainWidget.hpp&quot;

MainWidget::MainWidget(QWidget *parent)
: QWidget(parent)
{
    setWindowModality(Qt::ApplicationModal);
    
    timer = new QTimer(this);
    connect(timer, &amp;QTimer::timeout, this, &amp;MainWidget::onTimeChanged);
    timer-&gt;start(1000);

    cardLabel = new QLabel();
    cardLabel-&gt;setAlignment(Qt::AlignRight|Qt::AlignTop);
    cardLabel-&gt;setPixmap(QPixmap(&quot;:/icons/img&quot;));
}

MainWidget::~MainWidget()
{
    timer-&gt;stop();
    delete timer;
    delete cardLabel;
}

void MainWidget::onTimeChanged()
{
////
}

void MainWidget::slotFunc()
{
///
}

MainWidget.hpp

#ifndef MAINWINDOW_HPP
#define MAINWINDOW_HPP

//#include &lt;QMainWindow&gt;
#include &lt;QTimer&gt;
#include &lt;QWidget&gt;
#include &lt;QFile&gt;
#include &lt;QLabel&gt;
#include &lt;QLayout&gt;

typedef std::unique_ptr&lt;QMessageBox&gt; MessageBoxPtr;

class MainWidget : public QWidget
{
    Q_OBJECT

public:
    explicit MainWidget(QWidget *parent = nullptr);
    ~MainWidget();

protected slots:
    void slotFunc();

private slots:
    void onTimeChanged();

private:
    QLabel *cardLabel;
    QTimer *timer;
    QVBoxLayout *vbox;
    QHBoxLayout *hbox;
    bool isMessageBoxClosed = true;
};

#endif // MAINWINDOW_HPP

答案1

得分: 2

你不必delete一个QWidget,或者任何其他有父对象的QObject,它会由父对象的销毁来处理。

这就是为什么同时学习Qt和C++会令人困惑的原因。Qt将大多数常见的C++模式抛在窗外(不是小部件),一直停留在非常狭窄的C++子集上,直到最近它是C++98,现在是C++11。

如果你真的希望提前或动态删除一些具有父对象或连接(信号/槽)的QObject,你必须使用deleteLater()方法。名称可能会误导,如果可能的话,它会立即删除,但否则它会将删除排队,直到消息泵完成其循环。出于同样的原因,你几乎永远不应该将QObject声明为另一个QObject的直接成员 - 默认的析构函数行为会引起相同的问题。

析构函数会被调用,只是不是由你来调用。这样做的原因是框架是多线程的,父对象将通过信号/槽系统调用子对象的方法(“槽”)。如果你删除一个子对象,父对象将在其存储中具有悬空指针。

所以,问题的根本是MainWidget的创建。因为有这样的代码,

timer = new QTimer(this);
connect(timer, &amp;QTimer::timeout, this, &amp;MainWidget::onTimeChanged);

它应该要么在创建时指定父对象:

mainWidget = new MainWidget(this); // 这里不能完成,this不是QWidget

要么在销毁时删除:

mainWidget->deleteLater();

即使连接到自身也会计数,因为事件处理是全局的。

问题在于:Gui不是QWidget,而QWidget不能成为QObject的父对象。一个QObject托管小部件是相当不寻常的。通常由顶级小部件来完成。

注意:不应在其他线程中使用GUI元素。如果创建主机QObject的目的是将其传递/移动到工作线程,Qt的这条路上隐藏了更多的冰山。如果不是不可能的话,也非常不方便。

英文:

You don't have to delete a QWidget, or any other QObject, if it had a parent set. It will be done by parent's destruction.

That's the reason why studying Qt and C++ at same time is confusing. Qt throws out most of pudent C++ patterns out of window (not-a-widget), staying on very narrow subset of C++, until recently it was C++98, now it's C++11.

If you really wish to delete some parented or connected (signal\slot) QObject early or dynamically, you have to use deleteLater() method. The name is misleading, if possible it would delete immediately, but otherwise it will queue deletion until message pump finishes its cycle. For same reason you almost never should declate a QObject as a direct member of another QObject - default destructor behaviour would cause same problem.

Destructors would be called. Just not by you. The reason it's done this way is that the framework is multithreaded and parent will call methods ("slots") of a child object via signal\slot system. If you delete a child object, the parent would have a dangling pointer in its store.

So,t he root of the problem is creation of MainWidget. Because there is such code,

timer = new QTimer(this);
connect(timer, &amp;QTimer::timeout, this, &amp;MainWidget::onTimeChanged);

it should be either created with parent given:

mainWidget = new MainWidget(this); // can&#39;t be done here, this is not QWidget

or destroyed:

mainWidget-&gt;deleteLater();

Even connection to itself counts because event processing is global.

The problem here is: A Gui isn't a QWidget and a a QWidget cannot be parented to a QObject. It's rather unusual to have a QObject hosting widgets. It's normally done by a top-level widget.

NB: you shall not use GUI elements in other threads. If purpose of creating a host QObject was to pass\move it to a worker thread, there are more icebergs hiding in Qt on that route. IF not impossible, it's very inconvinient.

huangapple
  • 本文由 发表于 2023年6月22日 13:37:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/76528855.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定