Object Scope - Stack, Heap, and Bears - "oh MYY!"



  • I honestly don't even know where to begin. I'm completely new to QT but not new to C++. I've been working on the largest project I've done to date connecting a database to a desktop client application and manipulating data. I've only completed one small text only project in C++ years ago... so please forgive me if I seem dumb.

    I've been trying to figure out why the program is crashing upon exit I know it has to deal with scope and not understanding parent/child relationships and what SHOULD be declared on the stack vs the heap.

    Here is what the file format looks like:
    3 subclasses QSqlTableModel with their respective [three total .h && three total .cpp] - need this later on
    1 controller that subclasses QThread and another class in the same file for a worker [one .h && one .cpp]
    2 subclasses of QMainWindow with their respective [two total .h && two total .cpp] - for dif context menus
    1 subclass of QDialog [one .h && one .cpp]
    1 file for main [main.cpp]

    All of my dialogs & mainwindows have default constructors MainWindow() in their declaration. To start the program I'm creating a dbcontroller object on the stack in main and then calling my main dialog on the stack to then call the qmainwindows [which contain the custom QSqlTableModels] which then create qwidgets. All of this is done on the stack, which I'm not even sure if that could be okay and or normal practice.. The only thing on the heap is the contextmenu, menubar, and qactions in the qmainwindows, which I delete in their respective destructors.

    I have no experience with a debugger and have no idea how to go about debugging a problem like this other then using qDebug() to figure out which order things are deleted in...

    I know I'm doing something very wrong and I'm sure it's obvious.
    Looking at my qDebug()... I see my qDialog is deleted first that has objects for my qMainWindows... then qMainWindow is deleted then the program crashes. Is it possible to make the qMainWindows objects of a qDialog so that everything gets destructed properly or is there another problem?

    Please help!! I can post code if needed.

    Thanks so much,


  • Moderators

    @JahJerMar said in Object Scope - Stack, Heap, and Bears - "oh MYY!":

    All of this is done on the stack, which I'm not even sure if that could be okay and or normal practice..

    That's fine. Objects created on stack are destroyed when they go out of scope. Your main.cpp scope is global, so they will be deleted when the program ends. Just don't set any parents to these dialogs and db managers here.

    The only thing on the heap is the contextmenu, menubar, and qactions in the qmainwindows, which I delete in their respective destructors.

    If you also set their parents to something (like QMainWindow for example), then that is very likely the source of your crashes. Qt takes care of deleting objects within parent-child hierarchy. So, if you set parent in a widget - don't delete it. If you don't set parent - do manual deletion.

    If (as I suspect is the case here) you set parent and delete manually, then Qt will try to delete it again == crash.

    I have no experience with a debugger and have no idea how to go about debugging a problem like this other then using qDebug() to figure out which order things are deleted in...

    Just run your app in debug mode. When the app crashes, the debugger will show you exactly where it happens.

    Is it possible to make the qMainWindows objects of a qDialog so that everything gets destructed properly or is there another problem?

    Not sure what you mean here. Each QWidget (like main window, dialog, QLabel etc.) can act as a standalone window if if it has no parent set. Then you are responsible for deleting it. Each widget which has a parent, will be deleted when parent is deleted.

    Hope it helps :-) Ask more if something is still unclear. I've tried to keep things simple and clear, so I skipped some more gory details ;)



  • @sierdzio

    I think you answered a lot of my questions.

    I know there are a couple of ways that widgets get parented...

    "So, if you set parent in a widget - don't delete it."

    How will I know this happens? Layouts.... but what others?

    Also, when defining the default constructor as MainWindow(),when subclassing my objects,
    is this the same as MainWindow(QWidget* parent=0)? or no.

    Also

    Not sure what you mean here. Each QWidget (like main window, dialog, QLabel etc.) can act as a standalone >window if if it has no parent set. Then you are responsible for deleting it. Each widget which has a parent, will >be deleted when parent is deleted.

    I mean making the qmainwindow's children of qdialog and if that would seem... like a good design choice... but considering what you said it would just make sense to make everything a stand-alone window without a parent in my case.

    I changed all the QDialog/QMainWindow to a no parent constructor -- MainWindow(QWidget* parent=0)

    Picture of error.


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    It also happens when you call setParent. You should also check the documentation for mention like "takes ownership" which means that the object will be deleted appropriately.


  • Moderators

    @JahJerMar said in Object Scope - Stack, Heap, and Bears - "oh MYY!":

    I think you answered a lot of my questions.

    I know there are a couple of ways that widgets get parented...

    "So, if you set parent in a widget - don't delete it."

    How will I know this happens? Layouts.... but what others?

    Read the documentation if unsure. Qt always mentions if an implicit (hidden) parenting takes place - in general it is very rare.

    If you set your UI up using Qt Designer (or designer mode in Qt Creator), then all widgets will already be properly parented.

    In most cases of standard, simple apps, the only widget without parent is the main window. All the other widgets are children of the main window.

    If you have dialogs (you do), then well, it depends on how long you want them to live. If the static dialogs are enough for you (like https://doc.qt.io/qt-5/qfiledialog.html#static-public-members), then use them and you don't have to worry about parenting or deleting at all.

    Also, when defining the default constructor as MainWindow(),when subclassing my objects,
    is this the same as MainWindow(QWidget* parent=0)? or no.

    I don't know, please show your code, then I'll know exactly what you mean. Most probably the answer is yes.

    Also

    Not sure what you mean here. Each QWidget (like main window, dialog, QLabel etc.) can act as a standalone >window if if it has no parent set. Then you are responsible for deleting it. Each widget which has a parent, will >be deleted when parent is deleted.

    I mean making the qmainwindow's children of qdialog and if that would seem... like a good design choice... but considering what you said it would just make sense to make everything a stand-alone window without a parent in my case.

    I think you may be getting the relation a bit wrong. MainWindow is (usually - again I'm simplifying greatly) the "big daddy" that controls all other widgets and (quite often) other windows too. A QDialog with a parent will show as a separate window, but it will be centered on top of the parent.

    So, if anything, your dialogs should be children of the main window.

    Maybe try starting small - create a simple app with just a single window, play around with widgets a bit. It will get easier once you get more familiar with it.

    I changed all the QDialog/QMainWindow to a no parent constructor -- MainWindow(QWidget* parent=0)

    (https://ibb.co/k0x2gx)

    OK, now click through the call stack (the "Debugger" pane in your picture) and look what was happening in reverse order. Your MainFilmWindow crashed in destructor - that's a fairly good indication that you tried to delete it twice (possibly via the mechanism I described in previous post). The previous method which was invoked was your MainDialog - that suggests it was the dialog which tried to delete the main window. And lastly, you get to qMain, so maybe it happens when MainDialog goes out of scope (when app finishes).

    I'm just guessing, though, without the code.



  • This post is deleted!


  • @sierdzio

    I'm getting marked as spam for some reason when trying to post this code.

    main.cpp

    #include "maindialog.h"
    #include "mainwindow.h"
    #include "dbthreadcontroller.h"
    
    #include <QApplication>
    
    //check all includes please
    //        qDebug() << getenv("USERNAME"); get username
    
    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);
        qDebug() << QThread::currentThreadId() << "In qApp";
        app.setWindowIcon(QIcon(":/images/Conduent.ico"));
        QFont serifFont("Fantasy", 9);
        app.setFont(serifFont);
        qDebug() << "set up font and icon";
    
        dbController controller;
        qDebug() << "set up controller";
        MainDialog diag;
        qDebug() << "set up main dialog";
    
    
    
        return app.exec();
    }
    

    maincustomerwindow.cpp

    #ifndef MAINCUSTOMERWINDOW_H
    #define MAINCUSTOMERWINDOW_H
    
    #include "customersqltablemodel.h"
    #include <QMainWindow>
    #include <QtWidgets>
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget* parent=nullptr);
        ~MainWindow();
    
    signals:
    
    protected:
    
    
    #ifndef QT_NO_CONTEXTMENU
        void contextMenuEvent(QContextMenuEvent *event) override;
    #endif // QT_NO_CONTEXTMENU
    
    
    public slots:
        void resetSearch();
        void searchFor();
        void find();
        void resetView();
        void comboboxChange();
        void createCustomerListing();
        void createAddCustomer();
    
    private slots:
        void exit();
        int about();
    
    
    private:
        void createActions();
        void createMenus();
        void createStatusBar();
        void createToolBars();
        void setStatusBarMessage(const char* msg);
    
    
        QToolBar *fileToolBar;
        QMenu *fileMenu;
        QMenu *editMenu;
        QMenu *helpMenu;
        QAction *exitAct;
        QAction *copyAct;
        QAction *pasteAct;
        QAction *aboutAct;
        QAction *searchAct;
        QAction *resetAct;
    
        QLabel labelSearch;
        QLabel labelOne;
        QLabel labelTwo;
        QLabel labelThree;
        QWidget searchPanel;
        QWidget mainPanel;
        QWidget addCustomerPanel;
        QLabel options;
        QComboBox fieldOne;
        QComboBox fieldTwo;
        QComboBox fieldThree;
        QLineEdit editOne;
        QLineEdit editTwo;
        QLineEdit editThree;
        QPushButton search;
        QVBoxLayout *theLayout;
        QGridLayout *gridLayout;
        CustomerSqlTableModel customer;
    };
    
    #endif
    

    maincustomerwindow.cpp

    #include "maincustomerwindow.h"
    #include "maindialog.h"
    
    #include <QtWidgets>
    
    
    MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
    {
    
        hide();
        theLayout = new QVBoxLayout;
        gridLayout = new QGridLayout;
        setFixedSize(1600,800);
        setWindowTitle("Customer Listing");
    
        createActions();
        createStatusBar();
        createMenus();
        createToolBars();
    
        connect(&fieldOne, QOverload<int>::of(QComboBox::activated),
                this, &MainWindow::comboboxChange);
        connect(&fieldTwo, QOverload<int>::of(QComboBox::activated),
                this, &MainWindow::comboboxChange);
        connect(&fieldThree, QOverload<int>::of(QComboBox::activated),
                this, &MainWindow::comboboxChange);
    
        connect(&search, &QPushButton::clicked, this, &MainWindow::find);
    
        fieldOne.addItem(" ");
        fieldTwo.addItem(" ");
        fieldThree.addItem(" ");
        for (int c = 0; c < customer.columnCount(); ++c)
        {
            fieldOne.addItem(customer.headerData(c, Qt::Horizontal).toString());
            fieldTwo.addItem(customer.headerData(c, Qt::Horizontal).toString());
            fieldThree.addItem(customer.headerData(c, Qt::Horizontal).toString());
        }
        resetSearch();
        labelSearch.setText("Search by:");
        labelOne.setText("Field 1:");
        labelTwo.setText("Field 2:");
        labelThree.setText("Field 3:");
        labelOne.setBuddy(&fieldOne);
        labelTwo.setBuddy(&fieldTwo);
        labelThree.setBuddy(&fieldThree);
        editOne.setPlaceholderText("select");
        search.setText("Find");
        fieldOne.setEditable(false);
        fieldTwo.setEditable(false);
        fieldThree.setEditable(false);
        fieldOne.setInsertPolicy(QComboBox::NoInsert);
        fieldTwo.setInsertPolicy(QComboBox::NoInsert);
        fieldThree.setInsertPolicy(QComboBox::NoInsert);
    
        //theLayout.setParent(this); // avoiding crashing
    
        theLayout->addWidget(&labelSearch);
        theLayout->addWidget(&labelOne);
        theLayout->addWidget(&fieldOne);
        theLayout->addWidget(&editOne);
        theLayout->addWidget(&labelTwo);
        theLayout->addWidget(&fieldTwo);
        theLayout->addWidget(&editTwo);
        theLayout->addWidget(&labelThree);
        theLayout->addWidget(&fieldThree);
        theLayout->addWidget(&editThree);
        theLayout->addWidget(&search);
        searchPanel.setLayout(theLayout);
    
    
    
    }
    
    MainWindow::~MainWindow()
    {
        delete aboutAct;
        delete copyAct;
        delete pasteAct;
        delete searchAct;
        delete resetAct;
        delete exitAct;
        delete fileMenu;
        delete fileToolBar;
        delete gridLayout;
        delete theLayout;
        qDebug() << "Main window deleted";
    }
    
    void MainWindow::exit()
    {
    
    
    }
    
    int MainWindow::about()
    {
        QMessageBox msgBox;
        msgBox.setText("About this program.");
        msgBox.setInformativeText("This program was created by Jeremy Pacailler.");
        msgBox.setStandardButtons(QMessageBox::Ok);
        msgBox.setDefaultButton(QMessageBox::Ok);
        int ret = msgBox.exec();
        return ret;
    }
    
    void MainWindow::createActions()
    {
        exitAct = new QAction(tr("&Exit"), this);
        exitAct->setShortcuts(QKeySequence::Close);
        exitAct->setStatusTip(tr("Close the program"));
        connect(exitAct, &QAction::triggered, this, &MainWindow::close);
    
        aboutAct = new QAction(tr("&About"), this);
        aboutAct->setShortcuts(QKeySequence::AddTab);
        aboutAct->setStatusTip(tr("About the program"));
        connect(aboutAct, &QAction::triggered, this, &MainWindow::about);
    
        copyAct = new QAction (tr("&Copy"), this);
        copyAct->setShortcut(QKeySequence::Copy);
        copyAct->setStatusTip("Copy");
        //connect(copyAct, &QAction::triggered, this, &MainWindow::copy);
    
        pasteAct = new QAction (tr("&Paste"), this);
        pasteAct->setShortcut(QKeySequence::Paste);
        pasteAct->setStatusTip("Paste");
        //connect(pasteAct, &QAction::triggered, this, &MainWindow::paste);
    
        searchAct = new QAction (tr("&Search"), this);
        searchAct->setShortcut(QKeySequence::Save);
        searchAct->setStatusTip("Select search criteria");
        connect(searchAct, &QAction::triggered, this, &MainWindow::searchFor);
    
        resetAct = new QAction (tr("&Reset"), this);
        resetAct->setShortcut(QKeySequence::Redo);
        resetAct->setStatusTip("Reset view to default");
        connect(resetAct, &QAction::triggered, this, &MainWindow::resetView);
    }
    
    void MainWindow::createMenus()
    {
        fileMenu = menuBar()->addMenu(tr("&File"));
        fileMenu->addSeparator();
        fileMenu->addAction(exitAct);
    
        editMenu = menuBar()->addMenu(tr("&Edit"));
        editMenu->addAction(copyAct);
        editMenu->addAction(pasteAct);
    
        helpMenu = menuBar()->addMenu(tr("&Help"));
        helpMenu->addAction(aboutAct);
    }
    
    
    void MainWindow::createStatusBar()
    {
        statusBar()->showMessage(tr("Ready"));
    }
    
    void MainWindow::createToolBars()
    {
        fileToolBar = new QToolBar;
        fileToolBar = addToolBar(tr("Connect"));
        fileToolBar->setMovable(false);
        fileToolBar->addAction(exitAct);
    }
    
    void MainWindow::setStatusBarMessage(const char* msg)
    {
        statusBar()->showMessage(tr(msg));
    }
    
    
    
    #ifndef QT_NO_CONTEXTMENU
    void MainWindow::contextMenuEvent(QContextMenuEvent *event)
    {
        QMenu menu(this);
        menu.addAction(searchAct);
        menu.addAction(resetAct);
        menu.exec(event->globalPos());
    }
    #endif // QT_NO_CONTEXTMENU
    
    
    void MainWindow::createCustomerListing()
    {
        gridLayout->addWidget(customer.returnView(), 0, 0);
        mainPanel.setLayout(gridLayout);
        setCentralWidget(&mainPanel);
        show();
        activateWindow();
        raise();
    }
    
    void MainWindow::searchFor()
    {
        resetSearch();
        searchPanel.show();
        searchPanel.activateWindow();
        searchPanel.raise();
    
    }
    void MainWindow::resetView()
    {
        qDebug() << QThread::currentThreadId();
        customer.setFilter("");
        customer.select();
    }
    
    void MainWindow::find()
    {
        QString text;
    
    
        if (!(editOne.text().isEmpty()))
        {
            text.append(fieldOne.currentText());
            text.append(" = '");
            text.append(editOne.text());
            text.append("'");
        }
    
    
        if (!(editTwo.text().isEmpty()))
        {
            text.append(" AND ");
            text.append(fieldTwo.currentText());
            text.append(" = '");
            text.append(editTwo.text());
            text.append("'");
    
        }
        if (!(editThree.text().isEmpty()))
        {
            text.append(" AND ");
            text.append(fieldThree.currentText());
            text.append(" = '");
            text.append(editThree.text());
            text.append("'");
    
        }
    
        customer.setFilter(text);
        customer.select();
        qDebug() << text;
        customer.rowCount();
    }
    
    void MainWindow::comboboxChange()
    {
        switch(fieldOne.currentIndex())
        {
        case 0:
            break;
        default:
            break;
    
        }
    
    }
    
    void MainWindow::resetSearch()
    {
        fieldOne.setCurrentIndex(0);
        fieldTwo.setCurrentIndex(0);
        fieldThree.setCurrentIndex(0);
        editOne.setText(NULL);
        editTwo.setText(NULL);
        editThree.setText(NULL);
    }
    
    void MainWindow::createAddCustomer()
    {
    
    }

  • Lifetime Qt Champion

    As @sierdzio wrote, there's your problem: you have all your widgets on the stack, then they get parented when you setup your GUI. Therefore you'll have double deletes when it's destroyed. Your actions also becomes child of their corresponding menus so you should not delete them as they are handled by their respective menues.



  • @SGaist said in Object Scope - Stack, Heap, and Bears - "oh MYY!":

    As @sierdzio wrote, there's your problem: you have all your widgets on the stack, then they get parented when you setup your GUI. Therefore you'll have double deletes when it's destroyed. Your actions also becomes child of their corresponding menus so you should not delete them as they are handled by their respective menues.

    Thanks. I'm going to review everything you all said in detail and fix it. Thanks again.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.