QMainWindow

时间:2017-07-05 13:44:03

标签: c++ qt c++11 segmentation-fault c++14

首先我应该澄清一下,我的问题是,为什么在我使用std::unique_ptr的情况下会发生段错误,但是当我更改代码以使用new和delete时它不会发生?我在这里真的没有问题,因为段错是固定的,我知道我正在使用new和delete正确清理。我只是想知道为什么我不能使用std::unique_ptr

如果我在QMainWindow中创建std::unique_ptr个实例并且我的MainWindow创建了一个子窗口(例如打开QComboBoxToolTip),那么我的申请退出时会出现段错误。在这种情况下,如果我的MainWindow没有创建任何子窗口,则没有段错误。

如果我使用new和delete自己管理QMainWindow实例,则无论是否创建子窗口都不会出现段错误。

这是我的主要内容,它只是在我的run类上调用Bridge来处理创建我的MainWindow类的实例并启动它。

int main(int argc, char *argv[])
{
    Bridge bridge(argc, argv);
    bridge.run(); // Handles starting the main window
}

这是我的Bridge类的缩短版本导致段错(使用std :: unique_ptr我退出时会出现段错误):

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

这是没有segfault的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);
}

这是发生段错误时的反向跟踪:

还有一个关于QBasicTimer的奇怪打印输出,在我将代码更改为使用new而不是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 ()

还应该注意,此QApplication使用while循环运行并调用app.processEvents()而不是调用exec()。我意识到这不是最好的方法,但是这个应用程序是另一个应用程序的一部分,该应用程序轮询来自其他地方的事件,因为这个应用程序的性质比其他任何东西更“概念验证”,我想保持将它全部放在一个线程中就很简单了。请参阅下面的我的运行方法的片段:

while (isRunning()) {
    poller.poll(25);
    app.processEvents();
}

修改

MainWindow的构造函数中,我传递的this未被设置为父级(父级为nullptr):

头:

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

源:

MainWindow::MainWindow(BridgeInterface* interface, QWidget* parent) :
    QMainWindow(parent), pImpl(make_unique<IMPL>()) {

    pImpl->ui->setupUi(this);
    pImpl->bridgeInterface = interface;

    // Connect slots and stuff
}

3 个答案:

答案 0 :(得分:3)

删除MainWindow实例后,请勿删除QApplication。您需要以某种方式管理初始化和销毁​​顺序。

QApp::ctor Window::ctor Window::dtor QApp::dtor

可能的解决方案:直接在MainWindow

中销毁Bridge::~Bridge

P.S。您无需通过引用传递int argc

P.P.S。关于processEvents() - 你可以创建一个单独的线程并在那里运行你的Qt部分。可以使用GUI而不是主线程,如果你知道,你正在做什么:)。主要规则 - 应该从一个线程创建QApplication实例和所有GUI

答案 1 :(得分:1)

如果您没有为MainWindow发布代码,但很难说肯定,但我会说您要删除两次主窗口。

pImpl->mainwindow= new MainWindow(this);如果this是父级,则会在销毁后自动删除MainWindow,同样的事情会执行unique_ptr

使用原始指针并依赖Qt的父子清理或使用智能指针并且不将父级传递给MainWindow

答案 2 :(得分:1)

在您的课程中,在主窗口被销毁之前调用QApplication的析构函数。这是因为类变量按照它们列出的顺序被初始化,然后以相反的顺序被销毁。

如果你移动QApplication app;所以它是类中的第一个变量,它的析构函数将被调用到最后,在主窗口被销毁后,这是正确的行为。

当您明确删除它时它起作用的原因是您正在通过IMPL的析构函数删除mainwindow。 ~IMPL()在运行类变量析构函数之前运行,导致主窗口首先被销毁。