继承 QObject 的单例 - Qt

5

有没有可能创建一个继承自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 functionBluetoothManager::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_COPYClass(const Class &) Q_DECL_EQ_DELETE;\
     ^

1
尝试在BluetoothManager::GetInstance方法中返回指向您的BluetoothManager实例的指针,而不是实例本身。 - Tim Körner
为什么不使用引用? - s.paszko
你缺少了一些关键代码(这些代码使类成为单例)。 - juanchopanza
@s.paszko 参考文献完全没问题。 - juanchopanza
2
为什么不使用内置的单例宏 - Botje
显示剩余2条评论
3个回答

8
首先,您应该将构造函数设置为私有,以确保类只有一个实例,与单例模式的意图一致。如果允许每个人使用公共构造函数构建自己的实例,则不能将其称为单例。
然后,在您的实现中,您正在使用复制构造函数来初始化实例:
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 - Moia

1

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

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

如果您的类应该是单例类,则必须将其构造定义为私有,以便其他代码无法创建它的实例或使用 final 关键字继承它。(将构造函数设为私有已经完成了这个任务)。


即使您没有声明析构函数为虚函数(为了防止类被继承,应明确将其标记为“final”),它仍然保持虚函数。 - p-a-o-l-o
无论基类中的虚拟函数在其派生类中仍为虚拟函数,你可以将其标记为虚拟函数以使其更加清晰。 - p-a-o-l-o
@p-a-o-l-o 好的,我明白了。析构函数就像其他虚函数一样,在派生类中被重写并隐式地成为虚函数。谢谢。 - MRB

-1

是的,但 Singleton 的本质在于构造函数是私有的,因此您无法创建类的新实例。以下是一个快速而简短的示例,但您可以在此处查看更完整的示例:class PortableSettings

bluetoothmanager.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

bluetoothmanager.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。 - Moia
这对声誉非常危险。如果你有其他意思,请提出有力的论据,而不是妄自菲薄。 - Former contributor
更多的是空话,少量的实际论据。如果你从未使用过指向QObject实例的指针,那么你在Qt上的开发经验很少。 - Former contributor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接