Weird connection between QComboBox, QBasicTimer and static object initialization.
-
Hi, I am creating a basic widget class:
widget.h
class Widget : public QWidget { Q_OBJECT public: static Widget& inst(); Widget(); ~Widget() override = default; private: Ui::Form ui; };
widget.cpp
Widget& Widget::inst() { static Widget instance; return instance; } Widget::Widget() { ui.setupUi(this); }
The
Widget
class has a UI with just aQComboBox
with 2 items in it, it looks like this:
The
MainWindow
class has a UI with just aQPushButton
namedpushButton
to show theWidget
class object ui.
mainwindow.hclass MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(Widget* widPtr, QWidget *parent = nullptr); ~MainWindow(); private: Ui::MainWindowClass ui; QPointer<Widget> wid{nullptr}; public slots: void on_pushButton_clicked(); };
mainwindow.cpp
MainWindow::MainWindow(Widget* widPtr, QWidget* parent) : QMainWindow(parent) { ui.setupUi(this); wid = widPtr; } MainWindow::~MainWindow() {} void MainWindow::on_pushButton_clicked() { if(!wid.isNull()) wid->show(); }
If I do like this in my main function:
int main(int argc, char* argv[]) { QApplication a(argc, argv); auto wid{&Widget::inst()}; // getting widget instance, this is where the widget is created MainWindow w{wid}; w.show(); return a.exec(); }
And if I click on the
QComboBox
in theWidget
UI then I get an error only when I close the application:QBasicTimer::start: QBasicTimer can only be used with threads started with QThread Exception thrown at 0x00007FFE20BA447B (qwindowsd.dll) in ComboBox_Test.exe: 0xC0000005: Access violation reading location 0x0000000000000000.
However, if I do like this in the main function:
int main(int argc, char* argv[]) { QApplication a(argc, argv); Widget wid; // creating widget using normal constructor MainWindow w{&wid}; w.show(); return a.exec(); }
I do not get the error while closing.
There are a few peculier thing about the error:
- Only happens with
QComboBox
and only if I click it at least once during the program execution. I have tested the same setup but withQSpinBox
,QLineEdit
,QPushButton
, and none of these throw any kind of error when closing the app. - When the rror occurs I also get a warning about
QBasicTimer
although I am using no timers at all. - Error happens after the destructor for both
Widget
andMainWindow
have been called and only after it I get the warning aboutQBasicTimer
- This problem was not present around 3 weeks ago with Qt 6.3.0. Now this problem is appearing with both Qt 6.3.0 and Qt 6.4.2. I have updated both versions of Qt last week.
What is so peculiar about
QComboBox
? Why is it causing theQBasicTimer
to issue a warning? Why the error only occurs when I do a static initialization but not in normal construction even though the construction and destruction sequence and timings are same in both cases? So many questions... - Only happens with
-
@CJha
While you await someone's answer as to exact reason [ @Christian-Ehrlicher will probably like this one :) ]. Why do you do any of this static stuff, especially since it's causing a problem? Likely there would not be an issue if you did not? Your secondmain
function is the way everybody else would write it. -
@JonB I do it to create a Singleton, and I create a singleton because Qt itself creates a singleton of QApplication. The class object that I create in my main function before MainWindow deals a lot with
qApp
pointer and provides customized communication with the rest of the code, since QApplication is a singleton it makes sense to make this class a singleton as well. This is the only class I have as a singleton in the entire application. -
@CJha
OK, at least you have a reason :)Don't you think the reason it's erroring, under whatever circumstances, might be to do with when your singleton instance is getting destructed? If that comes after, say, the
QApplication a
instance is destroyed might that be the behaviour you see? You might connect to theQObject::destroyed
signal on the widget and on the app to see if this might be a factor? -
@JonB Thanks! I was checking for the destructor sequence of
Widget
andMainWindow
but never thought that theQApplication
object was being destroyed before theWidget
. And you are right, theQApplication
is getting destroyed first before theWidget
.I still don't understand why just clicking on only
QComboBox
will cause an error and not anything else likeQSpinBox
,QLineEdit
, etc. -
@CJha
QComboBox
I would guess will create a timer of some kind (thatQBasicTimer
?) to handle opening/closing the popup or something. Then something like that doesn't get destroyed till after the app with your singleton? The other widgets probably don't create one. Something like that? -
The general lifetime rule is that no QObject will live past the destruction of the application object. That basically means no static QObjects at all.
It's a blanket statement rule for all QObjects. The behavior, if you break it, is undefined. Some objects might actually make it, some won't, depending on what they do in their destructor. That's why QComboBox crashes while some other stuff might not. But in any case you shouldn't do it, even if something appears to work, because it might stop in next Qt version, on another platform or if the moon is in particular phase.
The reason this works
QApplication a(argc, argv); Widget wid;
is that you create both objects on the stack and it is guaranteed by the language that they will be destroyed in reverse order of creation, so
wid
will be destroyed beforea
.Static objects are destroyed in unspecified order at the end of the app, past the return point from the
main
function, so a singleton like yours is not valid.You can make a QObject singleton if you really want to like this:
Widget& Widget::inst() { static Widget* instance = nullptr; if (!instance) { instance = new Widget(); QObject::connect(qApp, &QApplication::aboutToQuit, instance, &Widget::deleteLater); } return *instance; }
but keep in mind that this is a bit dangerous if some code calls it out of the lifetime of the application object.
In general singleton pattern doesn't go too well with QObjects. -
@Chris-Kawa said in Weird connection between QComboBox, QBasicTimer and static object initialization.:
In general singleton pattern doesn't go too well with QObjects.
... and is not needed in most cases - this one here is one of the most cases where a singleton is not needed