【问题标题】:Singleton thread-safe programs单例线程安全程序
【发布时间】:2015-02-15 08:31:59
【问题描述】:

我正在尝试在 Qt 中编写一个具有单例设计的简单应用程序。下面是头文件:

#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

这是实现文件:

#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;
}

最后是main函数:

#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();
}

我的程序的构建是好的。但是我收到一个运行时错误“QWidget:必须在 QWidget 之前构造一个 QApplication”。我应该怎么做才能解决这个问题?我想用这种形式的单例来做一个线程安全的程序。

提前感谢您的帮助。

雷扎

【问题讨论】:

    标签: c++ qt singleton static-members


    【解决方案1】:

    保持全局对象实例线程安全的 Qt 惯用方式是通过Q_GLOBAL_STATIC。该实例是在第一次使用时创建的。这样,您的单例实例将在需要时创建,在 QApplication 实例存在之后。

    你应该写:而不是MainWindow* MainWindow::uniqueInstance = new MainWindow()

    Q_GLOBAL_STATIC(MainWindow, uniqueInstance);
    

    【讨论】:

    • 亲爱的@Kuba,感谢您的精彩回答,也感谢您的参考。
    • 亲爱的@Kuba,构造函数必须是公共的,以防万一,我们应该始终小心不要在主函数中“新建”任何其他对象。私有构造函数的解决方案是什么?
    【解决方案2】:

    根据之前的答案和http://doc.qt.io/qt-5/qglobalstatic.html#Q_GLOBAL_STATIC,答案如下:

    #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 文件如下:

    #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;
    }
    

    最后主文件如下:

    #include <QApplication>
    #include "mainwindow.h"
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
    
        MainWindow *w = MainWindow::getInstance();
        w->show();
    
        return a.exec();
    }
    

    这是有效的并且是线程安全的。

    【讨论】:

      【解决方案3】:
      MainWindow* MainWindow::uniqueInstance = new MainWindow();
      

      this是全局实例,发生在main函数之前,所以这个构造函数早于main函数,这是不允许的。 Qt需要先构造QApplication,然后是widget。所以你需要在main中构造QApplication之后移动它,或者只是删除它。

      MainWindow* MainWindow::uniqueInstance = 0;
      

      然后在 QApplication 之后构造对象。

      正如我所说,你不应该在 main 之前构造 uniqueInstance,根据你的 POST 修改一些代码,它应该可以工作。

      #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;
      }
      

      在Kuba的POST的基础上,引入了一个新的全局宏来创建这种SINGLETON(Q_GLOBAL_STATIChttp://qt-project.org/forums/viewthread/13977),更加优雅,但是这个宏只存在于Qt5,不存在Qt4,也有一些用处限制你应该注意。基本上它也是一个用于包装代码以在运行时创建单例的宏。

      【讨论】:

      • 亲爱的崔恒, 为这个问题上传了错误的代码是我的错误。现在请再看一下代码。
      • 亲爱的@Cui,您的代码正在运行,但它不是线程安全的。我也可以在 getInstance 函数中使用互斥锁,但出于某种原因,我也不想使用它。 getInstance 函数必须只返回一个指针。
      • @Reza 是的,乍一看它不是“线程安全的”,但你应该注意到,GUI 线程不是 retrannt,你可以在这里查看qt-project.org/doc/qt-4.8/threads-reentrancy.html。所以 GUI 线程不是一般线程,(如果其他线程尝试访问 GUI 数据,它通常会崩溃或收到运行时警告)。但是,如果你没有在QApplication之前初始化的全局变量Threads,那么你可以调用主单例方法,它是可以安全的。
      • 您正在以线程不安全的方式重新发明Q_GLOBAL_STATIC。这只是个坏主意。
      • 这个宏是Qt5提供的,Qt4没有。*
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-24
      相关资源
      最近更新 更多