简体   繁体   English

Qst将std :: unique_ptr用于QMainWindow时,退出时出现QT应用程序分段错误

[英]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创建子窗口(例如,打开QComboBoxToolTip ),则我的应用程序将在退出时出现段错误。 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 ): 在我的构造函数MainWindowthis我传递没有被设置为母(父是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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM