Take this example code:
#include <QWidget>
#include <QMainWindow>
#include <QList>
#include <QGridLayout>
#include <QLabel>
#include <QDialogButtonBox>
#include <QApplication>
class MainWindow;
class BaseWidget : public QWidget
{
public:
BaseWidget() = default;
virtual ~BaseWidget() {};
virtual void display(MainWindow *window) {}
};
class MainWindow : public QMainWindow
{
private:
QList<BaseWidget *> queue;
public:
void setWidget(BaseWidget *widget) {
if (queue.isEmpty() || queue.last() != widget) {
queue += widget;
}
this->setCentralWidget(widget);
}
public slots:
void back() {
queue.removeLast();
this->setWidget(queue.last());
}
};
class DerivedWidget2 : public BaseWidget
{
public:
void display(MainWindow *window) {
QGridLayout *layout = new QGridLayout(this);
QLabel *label = new QLabel(tr("hit \"Ok\" to segfault"));
layout->addWidget(label);
QDialogButtonBox *box = new QDialogButtonBox(QDialogButtonBox::Ok);
connect(box, &QDialogButtonBox::accepted, window, &MainWindow::back);
layout->addWidget(box);
window->setWidget(this);
}
};
class DerivedWidget : public BaseWidget
{
public:
void display(MainWindow *window) {
QGridLayout *layout = new QGridLayout(this);
QLabel *label = new QLabel(tr("hit \"Ok\" to move to the next"));
layout->addWidget(label);
QDialogButtonBox *box = new QDialogButtonBox(QDialogButtonBox::Ok);
connect(box, &QDialogButtonBox::accepted, window, [window] {
DerivedWidget2 *widget2 = new DerivedWidget2;
widget2->display(window);
});
layout->addWidget(box);
window->setWidget(this);
window->show();
}
};
int main(int argc, char **argv) {
QApplication app(argc, argv);
MainWindow *window = new MainWindow;
DerivedWidget *widget = new DerivedWidget;
widget->display(window);
return app.exec();
}
(Note that I've got a lot more derived widgets, so no I'm not gonna put a bunch of ifs and dynamic_cast
s. Also, all of this stuff would be in completely separate scopes/files.)
What I'm doing is storing these derived widgets in a "queue", where the last one is the currently displayed one, and you can go "back" in the queue, which will set the MainWindow
's central widget (i.e., which widget it displays to the user) to the previous widget in the queue.
So, everything seems to work fine, except one thing. When the widget is being displayed, in the queue list it stays as the derived class (according to my debugger). However, when I change the displayed widget, any widgets in the queue not currently being displayed seem to "degrade" into the base class (or at least that's what my debugger says), and it for some reason loses every single one of its properties. So, when I try to do something with that widget (like going back in the queue, back to that widget, and setting the main window to display it), it just segfaults.
Oddly enough, however, when I look in my debugger, there are other instances of that exact same widget pointer, pointing to the exact same memory address - they're just of the subclass type, while in the list it's "degraded" to the superclass.
Why is this happening? Anything I can do to fix it?
EDIT: Here's a video showing this with the debugger, should hopefully make it more clear.
EDIT 2: When the BaseWidget
class has actual members, when this segfault happens those members seem to all completely deallocate and "die", or turn into complete garbage in certain cases.