C ++ Singleton类创建多个实例

时间:2014-05-21 08:53:32

标签: c++ qt

使用鼠标事件调用静态方法GetUI,但是在调试中注意到,在一些非常罕见的情况下,鼠标事件发生时,构造函数被调用两次。

问题是调度程序在构造过程中停止,切换到另一个也开始创建另一个实例的任务进程调用?

    Object* Interface::instance = 0;

    Object* Interface::GetUI() {
        if (instance == 0) {
            instance = new Interface();
        }
        return instance;
    }

6 个答案:

答案 0 :(得分:4)

实际上你应该锁定单例,否则,当多线程时,你将创建多个实例 对于C ++ 11,您可以使用它,如下所示。

#include <mutex>

class Singleton 
{
    static Singleton *singletonInstance;
    Singleton() {}
    static std::mutex m_;

    public:

    static Singleton* getSingletonInstance()
    {
        std::lock_guard<std::mutex> lock(m_);
        if(singletonInstance == nullptr)
        {
            singletonInstance = new Singleton();
        }
        return singletonInstance;
    }
}

答案 1 :(得分:1)

问题是你之间有竞争条件 创建和对象的实例化。那里有两个 可能的解决方案;你可以同步GetUI函数, 正如Jerry YY建议的那样,或者你可以确保单身人士 在开始线程之前创建;只有竞争条件才会发生 当您在至少一个线程中修改对象时,一次 您已创建对象,instance永远不会被修改。

这样做的一种方法是简单地定义Interface::instance as:

Object* Interface::instance = Interface::GetUI();

零初始化确保在Interface::GetUI之前 调用Interface::instance初始化为null 指针,以及静态对象的初始化发生 在main之前。

否则:如果您确定Interface::GetUI永远不会 在进入main之前调用(看起来很可能,给出了什么 你已经说过了 - 你之前不应该有任何鼠标事件 输入main),然后您可以放弃测试(或替换为 assert( instance != nullptr中的Interface::GetUI) 写:

Object* Interface::instance = new Interface();

更简单,并避免所有问题。

答案 2 :(得分:1)

您描述的行为只能在多个线程使用GetUI时出现。我希望您知道,如果没有正确的锁定或使用排队方法调用,您无法直接调用GUI方法。

在Qt中创建全局变量的线程安全,惯用方法如下所示。实际上没有理由再次实现它。有时NIH很糟糕。

#include <QtGlobal>

class Object {};
class Interface {
public:
   static Object * GetUI();
};

// This must be in a *single* source file *only*. Not in header!
Q_GLOBAL_STATIC(Object, interfaceInstance)

Object* Interface::GetUI() {
    return interfaceInstance;
}

答案 3 :(得分:0)

这是一个带有模板的更好的单例实现:

template <class T>
class Singleton 
{
private:
  static T * _instance;
protected:
  Singleton (){}
public:
  static T & get()
  {
    if (_instance)
    {
      return *_instance;
    }
    _instance = new T();
    return *_instance;
  }
};

template <class T>  T* Singleton<T>::_instance = 0;

现在用继承实现它:

class Foo : public Singleton<Foo>

并在任何地方使用它:

Foo::get().DoSomething();

答案 4 :(得分:-1)

以下是earlier solution的更快版本(减少了互斥开销的使用)。

#include <mutex>

class Singleton 
{
    static volatile Singleton * singletonInstance;
    Singleton() {}
    static std::mutex m_;

    public:

    static Singleton* getSingletonInstance()
    {
        if(singletonInstance == nullptr)
        {
            std::lock_guard<std::mutex> lock(m_);
            if(singletonInstance == nullptr) {
                Singleton *tmp = new Singleton(); // fight with compiler see link in comment
                singletonInstance = tmp;
            }
        }
        return singletonInstance;
    }
}

无论如何,我不相信多线程是一个问题,因为你已经写过关于鼠标事件的文章,这应该只来自主线程。 添加断言Q_ASSERT(QThread::currentThread() == QCoreApplication::instance()->thread())以确保这是同步问题(如果检查调用堆栈失败)。

我建议也禁用assign operator和copy constructor。

答案 5 :(得分:-3)

您的实例和GetUI()方法必须是静态的。