[英]QT Application segmentation fault on exit when using std::unique_ptr for the QMainWindow
I should clarify first, my question is, why does the segfault happen in the case where I use a std::unique_ptr
but it doesn't when I change the code to use new and delete? 我首先要澄清一下,我的问题是,为什么在使用
std::unique_ptr
的情况下会发生段错误,但是当我更改代码以使用new和delete时却没有呢? I don't really have an issue here, since the segfault is fixed and I know I'm properly cleaning things up with new and delete. 我确实没有问题,因为段错误已修复,而且我知道我可以正确地使用new和delete清理内容。 I just want to know why I couldn't use a
std::unique_ptr
. 我只想知道为什么我不能使用
std::unique_ptr
。
If I create my QMainWindow
instance in a std::unique_ptr
and my MainWindow
creates a child window (eg opening a QComboBox
or ToolTip
), my application will segfault on exit. 如果我在
std::unique_ptr
创建QMainWindow
实例,并且MainWindow
创建子窗口(例如,打开QComboBox
或ToolTip
),则我的应用程序将在退出时出现段错误。 In this case, if my MainWindow
does not create any child windows, there is no segfault. 在这种情况下,如果我的
MainWindow
没有创建任何子窗口,则不会出现段错误。
If I manage my QMainWindow
instance myself with new and delete, there is no segfault regardless of whether or not I create a child window. 如果我使用new和delete自己管理
QMainWindow
实例,则无论是否创建子窗口,都不会出现段错误。
This is my main, it simply calls run
on my Bridge
class that handles creating the instance of my MainWindow
class and starting it. 这是我的主要知识,它只是调用在我的
Bridge
类上run
,该类处理创建MainWindow
类的实例并启动它。
int main(int argc, char *argv[])
{
Bridge bridge(argc, argv);
bridge.run(); // Handles starting the main window
}
This is a shortened version of my Bridge
class that results in a segfault (using std::unique_ptr I get a segfault on exit): 这是我的
Bridge
类的简化版本,导致segfault(使用std :: unique_ptr,我在退出时得到segfault):
class Bridge::IMPL {
public:
IMPL(int& argc, char ** argv) :
mainwindow(), isRunning(true), app(argc, argv) {}
~IMPL() = default;
public:
std::unique_ptr<MainWindow> mainwindow;
bool isRunning;
QApplication app;
};
Bridge::Bridge(int& argc, char ** argv) :
pImpl(make_unique<IMPL>(argc, argv)) {
pImpl->mainwindow.reset(new MainWindow(this));
}
This is the Bridge class with no segfault (slightly modified to remove the std::unique_ptr
and instead use new and delete. This version of the Bridge
class DOES NOT CRASH): 这是没有段错误的Bridge类(稍作修改即可删除
std::unique_ptr
,而使用new和delete。此版本的Bridge
类不崩溃):
class Bridge::IMPL {
public:
IMPL(int& argc, char ** argv) :
mainwindow(nullptr), isRunning(true), app(argc, argv) {}
~IMPL() {
if (mainwindow) {
delete mainwindow;
}
}
public:
MainWindow* mainwindow;
bool isRunning;
QApplication app;
};
Bridge::Bridge(int& argc, char ** argv) :
pImpl(make_unique<IMPL>(argc, argv)) {
pImpl->mainwindow= new MainWindow(this);
}
This is the back-trace when a segfault would happen: 这是发生段错误的追溯:
Also there is a weird print-out about QBasicTimer
that doesn't get printed after I changed the code to use new and delete instead of std::unique_ptr
还有关于
QBasicTimer
的怪异打印输出,在我将代码更改为使用new和delete而不是std::unique_ptr
以后,没有打印出来
QBasicTimer::start: QBasicTimer can only be used with threads started with QThread
Thread 1 "application" received signal SIGSEGV, Segmentation fault.
0x00007fffe75bf2e2 in ?? () from /home/user/Qt/5.9/gcc_64/plugins/platforms/../../lib/libQt5XcbQpa.so.5
(gdb) bt
#0 0x00007fffe75bf2e2 in ?? () from /home/user/Qt/5.9/gcc_64/plugins/platforms/../../lib/libQt5XcbQpa.so.5
#1 0x00007fffe75bf5c4 in ?? () from /home/user/Qt/5.9/gcc_64/plugins/platforms/../../lib/libQt5XcbQpa.so.5
#2 0x00007fffe75b9669 in QXcbConnection::removeWindowEventListener(unsigned int) () from /home/user/Qt/5.9/gcc_64/plugins/platforms/../../lib/libQt5XcbQpa.so.5
#3 0x00007fffe75ceafa in QXcbWindow::destroy() () from /home/user/Qt/5.9/gcc_64/plugins/platforms/../../lib/libQt5XcbQpa.so.5
#4 0x00007fffe75cec07 in QXcbWindow::~QXcbWindow() () from /home/user/Qt/5.9/gcc_64/plugins/platforms/../../lib/libQt5XcbQpa.so.5
#5 0x00007fffe43ce2ee in ?? () from /home/user/Qt/5.9/gcc_64/plugins/xcbglintegrations/libqxcb-glx-integration.so
#6 0x00007ffff4b56f46 in QWindowPrivate::destroy() () from /home/user/Qt/5.9/gcc_64/lib/libQt5Gui.so.5
#7 0x00007ffff534ecd7 in QWidgetPrivate::deleteTLSysExtra() () from /home/user/Qt/5.9/gcc_64/lib/libQt5Widgets.so.5
#8 0x00007ffff53524d8 in QWidget::destroy(bool, bool) () from /home/user/Qt/5.9/gcc_64/lib/libQt5Widgets.so.5
#9 0x00007ffff53598b0 in QWidget::~QWidget() () from /home/user/Qt/5.9/gcc_64/lib/libQt5Widgets.so.5
#10 0x00007ffff541ae7a in ?? () from /home/user/Qt/5.9/gcc_64/lib/libQt5Widgets.so.5
#11 0x00007ffff4580b83 in QObjectPrivate::deleteChildren() () from /home/user/Qt/5.9/gcc_64/lib/libQt5Core.so.5
#12 0x00007ffff5359894 in QWidget::~QWidget() () from /home/user/Qt/5.9/gcc_64/lib/libQt5Widgets.so.5
#13 0x00007ffff540eac9 in QComboBox::~QComboBox() () from /home/user/Qt/5.9/gcc_64/lib/libQt5Widgets.so.5
#14 0x00007ffff4580b83 in QObjectPrivate::deleteChildren() () from /home/user/Qt/5.9/gcc_64/lib/libQt5Core.so.5
#15 0x00007ffff5359894 in QWidget::~QWidget() () from /home/user/Qt/5.9/gcc_64/lib/libQt5Widgets.so.5
#16 0x00007ffff5359ab9 in QWidget::~QWidget() () from /home/user/Qt/5.9/gcc_64/lib/libQt5Widgets.so.5
#17 0x00007ffff4580b83 in QObjectPrivate::deleteChildren() () from /home/user/Qt/5.9/gcc_64/lib/libQt5Core.so.5
#18 0x00007ffff5359894 in QWidget::~QWidget() () from /home/user/Qt/5.9/gcc_64/lib/libQt5Widgets.so.5
#19 0x00007ffff5359ab9 in QWidget::~QWidget() () from /home/user/Qt/5.9/gcc_64/lib/libQt5Widgets.so.5
#20 0x00007ffff4580b83 in QObjectPrivate::deleteChildren() () from /home/user/Qt/5.9/gcc_64/lib/libQt5Core.so.5
#21 0x00007ffff5359894 in QWidget::~QWidget() () from /home/user/Qt/5.9/gcc_64/lib/libQt5Widgets.so.5
#22 0x0000000000421ed4 in MainWindow::~MainWindow() ()
#23 0x0000000000421f0e in MainWindow::~MainWindow() ()
#24 0x0000000000421698 in std::default_delete<MainWindow>::operator()(MainWindow*) const ()
#25 0x0000000000421171 in std::unique_ptr<MainWindow, std::default_delete<MainWindow> >::~unique_ptr() ()
#26 0x000000000042191c in Bridge::IMPL::~IMPL() ()
#27 0x0000000000421942 in std::default_delete<Bridge::IMPL>::operator()(Bridge::IMPL*) const ()
#28 0x0000000000421387 in std::unique_ptr<Bridge::IMPL, std::default_delete<Bridge::IMPL> >::~unique_ptr() ()
#29 0x0000000000420cb0 in Bridge::~Bridge() ()
#30 0x0000000000421c41 in main ()
It should also be noted that this QApplication
is run using a while loop and calls to app.processEvents()
rather than calling exec()
. 还应注意,此
QApplication
使用while循环运行,并调用app.processEvents()
而不是调用exec()
。 I realize that's not the best way to do it, however this app is part of another application that polls for events from somewhere else and since the nature of this application is more "proof-of-concept" than anything else, I wanted to keep it simple by having it all in one thread. 我意识到这不是最好的方法,但是此应用程序是另一个应用程序的一部分,该应用程序从其他地方轮询事件,并且由于此应用程序的性质比其他任何事物都更“概念验证”,因此我想保留通过将它们全部集中在一个线程中就很简单。 See below for a snippet of my run method:
请参阅以下我的run方法的代码段:
while (isRunning()) {
poller.poll(25);
app.processEvents();
}
EDIT: 编辑:
In my constructor for MainWindow
the this
I'm passing is not being set as the parent (the parent is nullptr
): 在我的构造函数
MainWindow
的this
我传递没有被设置为母(父是nullptr
):
header: 标题:
class MainWindow: public QMainWindow
{
Q_OBJECT
public:
class BridgeInterface{
public:
// Some pure virtual methods here
};
explicit MainWindow(BridgeInterface* interface, QWidget* parent = nullptr);
// some more stuff...
private:
class IMPL;
std::unique_ptr<IMPL> pImpl;
};
source: 资源:
MainWindow::MainWindow(BridgeInterface* interface, QWidget* parent) :
QMainWindow(parent), pImpl(make_unique<IMPL>()) {
pImpl->ui->setupUi(this);
pImpl->bridgeInterface = interface;
// Connect slots and stuff
}
Don't delete MainWindow
after deleting QApplication
instance. 删除
QApplication
实例后不要删除MainWindow
。 You need to somehow manage initialization and destruction order. 您需要以某种方式管理初始化和销毁顺序。
QApp::ctor Window::ctor Window::dtor QApp::dtor
Possible solution: destroy MainWindow
directly inside Bridge::~Bridge
可能的解决方案:直接在
Bridge::~Bridge
MainWindow
内部销毁MainWindow
PS you don't need to pass int argc
via reference. PS,您不需要通过引用传递
int argc
。
PPS about processEvents() - you may create a separated thread and run your Qt part there. 关于processEvents()的PPS-您可以创建一个单独的线程并在其中运行您的Qt部分。 It is OK to use GUI not from main thread, ofc if you know, what you are doing :).
可以不从主线程使用GUI,如果您知道自己在做什么,可以使用OFc :)。 Main rule - QApplication instance and all GUI should be created from one thread
主要规则-QApplication实例和所有GUI应该从一个线程创建
Difficult to tell for sure given you did not post code for MainWindow but I'd say you are deleting mainwindow twice. 鉴于您没有为MainWindow发布代码,因此很难确定,但我要说您要两次删除mainwindow。
pImpl->mainwindow= new MainWindow(this);
if this
is a parent it will delete MainWindow automatically upon destruction and same thing will do the unique_ptr 如果
this
是父项,它将在销毁时自动删除MainWindow,并且相同的操作也会执行unique_ptr
Either use raw pointers and rely on Qt's parent-child cleanup or use smart pointers and do not pass a parent to MainWindow 使用原始指针并依靠Qt的父子清理,或者使用智能指针并且不将父传递给MainWindow
In your class, QApplication's destructor is being called before your mainwindow is destroyed. 在您的类中,将在销毁主窗口之前调用QApplication的析构函数。 That's because class variables are initalized in the order they're listed, then destroyed in the opposite order.
这是因为类变量按照列出的顺序初始化,然后以相反的顺序销毁。
If you move QApplication app;
如果您移动
QApplication app;
so it's the first variable in the class, its destructor will be called last, AFTER mainwindow is destroyed, which is the correct behavior. 因此它是类中的第一个变量,其析构函数将被最后调用,AFTER主窗口被销毁后,这是正确的行为。
The reason it works when you delete it explicitly is that you're deleting mainwindow
via the IMPL's destructor. 显式删除它的原因是要通过IMPL的析构函数删除
mainwindow
窗口。 ~IMPL() runs before the class variable destructors are run, resulting in mainwindow being destroyed first. 〜IMPL()在类变量析构函数运行之前运行,导致主窗口首先被破坏。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.