c++qtqobjectownership-semantics

Why is there no crash when assigning a member widget as the central widget in QMainWindow?


I wrote the following code, which doesn't provoke any error:

main.cpp

#include "main_window.hpp"
#include <QtWidgets/QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

main_window.hpp

#pragma once

#include "ui_main_window.h"

#include <QtWidgets/QMainWindow>
#include <QGraphicsView>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    {
        ui.setupUi(this);
        setCentralWidget(&view);
    }

private:
    Ui::MainWindowClass ui;
    QGraphicsView       view;
};

However, Qt documentation for QMainWindow::setCentralWidget says that

Note: QMainWindow takes ownership of the widget pointer and deletes it at the appropriate time.

Shouldn't there be a crash on finishing app because of double deleting of view: by Qt's ownership model and by ~MainWindow()?


Solution

  • This would work as written. The behavior is well defined.

    The way Qt ownership works, every QObject-derived object keeps a list of the children it owns, as well as a pointer to its parent it is owned by. QObject destructor then does two things - it destroys all children, and also notifies its parent that it has been destroyed. In response to that, the parent removes this now-destroyed child from its list of owned objects.

    In your example, when MainWindow object is constructed, the base class QObject constructor runs first, (then other base class constructos), and then constructors of data members, including view. The destruction proceeds in the reverse order: view is destroyed first, whereupon it notifies its owner MainWindow object (or rather, the QObject base class sub-object within MainWindow) to remove itself from the list. Then eventually QObject destructor runs, but by that time a pointer to view is no longer in the list.

    By this mechanism, double deletion is avoided.