英文:
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 "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
答案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, &QTimer::timeout, this, &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, &QTimer::timeout, this, &MainWidget::onTimeChanged);
it should be either created with parent given:
mainWidget = new MainWidget(this); // can't be done here, this is not QWidget
or destroyed:
mainWidget->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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论