【问题标题】:Singleton with QObject inheritance - Qt具有 QObject 继承的单例 - Qt
【发布时间】:2019-12-19 18:24:35
【问题描述】:

是否可以创建具有 QObject 继承的单例类?添加 QObject 继承后出现编译错误。也许问题在于静态单例创建(应该是动态的)?这是我的方法

标题

#ifndef BLUETOOTHMANAGER_H
#define BLUETOOTHMANAGER_H

#include <QObject>

class BluetoothManager : public QObject
{
    Q_OBJECT
public:
    virtual ~BluetoothManager() {}

    /// Static getter
    static BluetoothManager & GetInstance()
    {
        return instance;
    }

private:
    /// static Bluetooth manager instance
    static BluetoothManager instance;

    explicit BluetoothManager(QObject * parent);
};

#endif // BLUETOOTHMANAGER_H

还有cpp文件

#include "BluetoothManager.h"

/// singleton creation
BluetoothManager BluetoothManager::instance = BluetoothManager(static_cast<QObject*>(nullptr));

BluetoothManager::BluetoothManager(QObject * parent)
    : QObject(parent)
{

}

在编译过程中出现错误

../QtHealthApp/network/bluetooth/BluetoothManager.cpp:4:94: error: use of deleted function ‘BluetoothManager::BluetoothManager(const BluetoothManager&)’  BluetoothManager BluetoothManager::instance = BluetoothManager(static_cast<QObject*>(nullptr));
                                                                                              ^ In file included from /opt/Qt5.12.LTS/5.12.6/gcc_64/include/QtCore/qnamespace.h:43:0,
                 from /opt/Qt5.12.LTS/5.12.6/gcc_64/include/QtCore/qobjectdefs.h:48,
                 from /opt/Qt5.12.LTS/5.12.6/gcc_64/include/QtCore/qobject.h:46,
                 from /opt/Qt5.12.LTS/5.12.6/gcc_64/include/QtCore/QObject:1,
                 from ../QtHealthApp/network/bluetooth/BluetoothManager.h:4,
                 from ../QtHealthApp/network/bluetooth/BluetoothManager.cpp:1: ../QtHealthApp/network/bluetooth/BluetoothManager.h:33:20: note: declared here
     Q_DISABLE_COPY(BluetoothManager)
                    ^ /opt/Qt5.12.LTS/5.12.6/gcc_64/include/QtCore/qglobal.h:372:5: note: in definition of macro ‘Q_DISABLE_COPY’
     Class(const Class &) Q_DECL_EQ_DELETE;\
     ^

【问题讨论】:

  • 尝试返回指向您的BluetoothManager 实例而不是实例本身的指针(在BluetoothManager::GetInstance 方法中)。
  • 为什么不参考?
  • 您遗漏了一些关键代码(使该类成为单例的原因)。
  • @s.paszko 参考很好。
  • 为什么不使用built-in singleton macro

标签: c++ qt


【解决方案1】:

首先,您应该将构造函数设为私有,这与单例模式的意图一致,以确保一个类只有一个实例。如果您让每个人都使用公共构造函数构建自己的实例,则不能将您的类称为单例。

然后,在您的实现中,您将使用复制构造初始化您的实例:

BluetoothManager BluetoothManager::instance = BluetoothManager(static_cast<QObject*>(nullptr));

你不能这样做,因为 QObject 已经删除了复制构造函数(毕竟在制作单例时这很好)。

只需给构造函数参数一个默认值:

explicit BluetoothManager(QObject * parent = nullptr);

这样您的实例定义就可以是:

BluetoothManager BluetoothManager::instance; 

问题应该消失了。

另外,我建议你在 c++ 中使用一个非常流行的单例变体,这是众所周知的避免static initialization order ‘fiasco’:将静态实例从类作用域移动到GetInstance 函数作用域:

class BluetoothManager : public QObject
{
    Q_OBJECT
public:

    virtual ~BluetoothManager() {}

    /// Static getter
    static BluetoothManager & GetInstance()
    {
        static BluetoothManager instance;
        return instance;
    }

private:
    explicit BluetoothManager(QObject * parent = nullptr){}
};

【讨论】:

  • 我建议为类使用 final 说明符,为析构函数使用 override = default
【解决方案2】:

QObject 不可复制,您不能在此对象中调用复制构造,但在您的静态字段初始化中,您正在构造您的对象,然后将其分配给调用您类型的复制构造的静态字段。

相反,您可以使用静态指针或智能指针并使用new 对其进行初始化。

如果你的类应该是一个单例类,你必须将它的构造定义为私有的,这样其他代码就不能创建它的实例或使用final 关键字继承它。 (将构造函数设为私有已经完成了)。

【讨论】:

  • 析构函数保持虚拟,即使你没有声明它是这样的(为了防止类被继承,应该明确地将它标记为final)。
  • 无论基类中的虚拟在其派生类中保持虚拟,我们都可以将其标记为虚拟以使其清楚。
  • @p-a-o-l-o 好的,我明白了,析构函数就像任何其他虚函数一样在派生类中被重写并且是隐式虚拟的。谢谢。
【解决方案3】:

是的,但是 Singleton 的本质是构造函数是私有的,所以你不能创建类的新实例。下面是一个简单的小示例,但您可以在此处查看更完整的示例:class PortableSettings

蓝牙管理器.h

#ifndef BLUETOOTHMANAGER_H
#define BLUETOOTHMANAGER_H

#include <QObject>

class BluetoothManager : public QObject
{
    Q_OBJECT

public:
    static BluetoothManager* instance();

    void doSomething();

private:
    explicit BluetoothManager(QObject *parent = nullptr);

};

#endif // BLUETOOTHMANAGER_H

蓝牙管理器.cpp

#include "bluetoothmanager.h"

BluetoothManager *BluetoothManager::instance()
{
    static BluetoothManager inst;
    return &inst;
}

void BluetoothManager::doSomething()
{
    // ...
}

BluetoothManager::BluetoothManager(QObject *parent) : QObject(parent)
{
    // initialization
}

main.cpp

#include <QCoreApplication>
#include "bluetoothmanager.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    BluetoothManager::instance()->doSomething();
    return 0;
}

【讨论】:

  • 返回指针是危险的,不是实现单例的正确方法,即使使用 qobjects 也是如此
  • 这对声誉很危险,主要是。如果你的意思是别的,那就公开可靠的论据而不是自以为是。
  • 更多的冠冕堂皇的论点和一些坚实的论据。如果您从未使用过指向 QObject 实例的指针,那么您对 ​​Qt 的开发很少。
猜你喜欢
  • 2011-09-24
  • 2011-09-12
  • 2021-11-02
  • 2012-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-10
相关资源
最近更新 更多