0

I'm trying to write a simple app with singleton design in Qt. Below is the header file:

#ifndef MAINWINDOW_H

#define MAINWINDOW_H

#include <QMainWindow>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    static MainWindow *getInstance();
    ~MainWindow();

private:
    explicit MainWindow(QWidget *parent = 0);
    static MainWindow *uniqueInstance;
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

And here is implementation file:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow* MainWindow::uniqueInstance = new MainWindow();


MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

MainWindow* MainWindow::getInstance()
{
    return uniqueInstance;
}

Finally here is the main function:

#include <QApplication>
#include "mainwindow.h"

#include "QThread"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    MainWindow *w = MainWindow::getInstance();
    w->show();

    return a.exec();
}

Building of my program is OK. But I receive a run-time error "QWidget: Must construct a QApplication before a QWidget". What should I do for solving this problem? I want to use this form of singleton to have a thread-safe program.

Thanks in advance for your helps.

Reza

Reza
  • 3,473
  • 4
  • 35
  • 54

3 Answers3

2

The Qt-idiomatic way of holding a global object instance thread-safely is through Q_GLOBAL_STATIC. The instance is created on the first use. This way, your singleton instance will be created when needed, after QApplication instance exists.

Instead of MainWindow* MainWindow::uniqueInstance = new MainWindow(), you'd write:

Q_GLOBAL_STATIC(MainWindow, uniqueInstance);
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • Dear @Kuba, Thanks for your nice answer and also thanks for your reference. – Reza Feb 17 '15 at 07:23
  • Dear @Kuba, the constructor must be public in case and we should always be careful to not "new" any other object in main function. What would be the solution for private constructor? – Reza Feb 17 '15 at 08:06
  • Reading from the docs, this initializes the object thread safe, but will it also make sure calls to functions of the objects are done in thread-safe manner? Because it does not appear so (and was maybe not the question of OP, but Google leads you here) – IceFire May 05 '23 at 06:47
1

Based on previous answers and also http://doc.qt.io/qt-5/qglobalstatic.html#Q_GLOBAL_STATIC, the answer is like below:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT
    friend class myClass;

public:
    static MainWindow *getInstance();
    ~MainWindow();

private:

    explicit MainWindow(QWidget *parent = 0);
    Ui::MainWindow *ui;
};

class myClass : public MainWindow
{
};

#endif // MAINWINDOW_H

.cpp file is like below:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QGlobalStatic>

Q_GLOBAL_STATIC(myClass,uniqueInstance)

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

MainWindow* MainWindow::getInstance()
{
    return uniqueInstance;
}

finally main file is like below:

#include <QApplication>
#include "mainwindow.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    MainWindow *w = MainWindow::getInstance();
    w->show();

    return a.exec();
}

And that works and is thread-safe.

Reza
  • 3,473
  • 4
  • 35
  • 54
-1
MainWindow* MainWindow::uniqueInstance = new MainWindow();

this is global instance, it happens before the main function, so this constructor is earlier than the main function, which is not allowed. Qt needs to construct QApplication first, then the widget. so you need to move this after constructor the QApplication in main, or just remove it.

MainWindow* MainWindow::uniqueInstance = 0;

then constructor the object after QApplication.

As I said, you should NOT constructor the uniqueInstance before main, modified some code basing on your POST, it should work.

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow* MainWindow::uniqueInstance = 0;


MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

MainWindow* MainWindow::getInstance()
{
    if (0 == MainWindow::uniqueInstance){
        MainWindow::uniqueInstance = new MainWindow();
    }
    return MainWindow::uniqueInstance;
}

Basing on Kuba's POST, there is introduced a new global MACRO for creating this kind of SINGLETON (Q_GLOBAL_STATIC http://qt-project.org/forums/viewthread/13977), it is more elegant, however, this macro only exists in Qt5, not Qt4, and also has some usage limit you should notice. Basically it is also a macro for wrapping code to create singleton at runtime.

Cui Heng
  • 1,265
  • 8
  • 10
  • Dear Cui Heng, It was my mistake to upload a wrong code for this question. Now please take a look at the code again. – Reza Feb 15 '15 at 08:48
  • Dear @Cui, You code is working but it's not thread safe. I can also use Mutex locker in getInstance function, but for some reason I don't want to use that too. The getInstance function must return only one pointer. – Reza Feb 15 '15 at 10:01
  • @Reza Yes, it is not "Thread-safe" in the 1st looking, but you should notice that, the GUI thread is not retrannt, you can have a look here http://qt-project.org/doc/qt-4.8/threads-reentrancy.html. so GUI thread is not a general thread, (it usually crash or got a runtime warning if other thread try to access the GUI data). However, if you don't have global variables Threads which are init before the QApplication, then you can call the main singleton method, it's OK and safe. – Cui Heng Feb 15 '15 at 10:10
  • 1
    You're reinventing `Q_GLOBAL_STATIC` in a thread-unsafe way. That's just a bad idea. – Kuba hasn't forgotten Monica Feb 15 '15 at 19:57
  • This macro is provided in Qt5, not available at Qt4.* – Cui Heng Feb 15 '15 at 23:15
  • Still, what you're doing is not thread-safe. [This question](http://stackoverflow.com/q/2576022/1329652) provides all that's needed in this area. – Kuba hasn't forgotten Monica Feb 16 '15 at 19:11