2012-02-10 87 views
9

我通常习惯于实现以这种方式Singleton模式,因为它是如此简单:这个单例实现有问题吗?

class MyClass 
{ 
    public: 
     MyClass* GetInstance() 
     { 
      static MyClass instance; 
      return &instance; 
     } 

    private: 
     //Disallow copy construction, copy assignment, and external 
     //default construction. 
}; 

这似乎不是创建一个静态实例指针,在源文件中初始化它,并使用动态内存显著更容易带有警卫的实例功能分配。

有没有我看不到的缺点?它对我来说看起来是线程安全的,因为我认为第一个线程到达第一个线程会导致实例化 - 而且它看起来很好和简洁。我想,必须有我没有看到,因为这虽然是不常见的一个问题 - 我想获得一些反馈之前,我一直用它

+2

你可以从你的事实,这是一个单身返回MyClass的和,而不是MyClass的* – 2012-02-10 14:10:52

+10

你的意思是,除了? HTTP://计算器。com/q/137975/10077 – 2012-02-10 14:19:30

+0

是的,是的,我知道他们是一个反模式,应该避免:)我仍然感兴趣,为什么这可能不是线程安全的。我知道修改单身人士的数据成员应该是互斥守卫的,但我认为这是通过这个函数创建的是安全的。 – 2012-02-10 14:57:38

回答

3

这不是一个固有的线程解决方案:在创建实例,另一个线程可以抢占,并尝试获取实例,导致无论是双实例或使用未构造的实例。

这是由几个编译器中加入保护处理(gcc的,我觉得这是禁用此标记),因为没有办法与用户定义的互斥守护这一点。

+0

所以,你是说即使知道这里有一个静态变量,它可以创建多个不同的变量? – 2012-02-10 14:54:27

+0

实例在首次使用该函数时被初始化。我猜这比双重初始化更加双倍。 – stefaanv 2012-02-10 14:56:31

+0

我原以为用静态命中行的第一个线程会触发它的创建/初始化,并且由于静态如何工作,第二个线程触及该行将完全意识到创建和初始化。为什么不是这样呢? – 2012-02-10 14:59:10

0

除了在多线程的情况 - 没有。那么 - 让我认定这一点,建设是懒惰的(所以第一个电话可能会有一个打击)和破坏 - 嗯,没有保证(除了它会 - 在某些时候)

3

缺点是,你有不准确控制对象何时被销毁。如果其他静态对象试图从析构函数访问它,这将会是一个问题。

符合C++ 11的编译器必须以线程安全的方式实现此功能;但是,较旧的编译器可能不会。如果您有疑问,并且您并不特别想要懒惰的初始化,则可以在启动任何线程之前通过调用访问器来强制创建对象。

+0

那么,你是说即使函数中存在静态,在非C++ 11编译器中它可以被双重创建?但只要我在进入代码的多线程阶段之前调用实例函数,它就是线程安全的创建?只是澄清:)感谢迄今为止的答案! – 2012-02-10 15:00:36

+0

@ w00te:是的,没错。在C++ 11之前,标准对线程安全没有什么可说的,所以完全取决于编译器实现是否防止双重创建。它应该是线程安全的最合理的最近编译器。 – 2012-02-10 15:46:38

+0

+1感谢您的帮助,很高兴知道它是特定于标准版本的。 – 2012-02-10 16:31:22

2

有在界面的两个问题:

  • 您应该返回参考
  • 你应该使其中的一个析构函数或delete操作private

此外,有轻微的风险试图在它被破坏后使用这个类。

关于你的多线程关注(和初始化我猜的):这是在C++ 11和罚款已经细很长一段时间良好的C++编译器反正。

+0

+1谢谢你的帮助:) – 2012-02-11 02:40:59

0

一般来说,预选赛static在方法局部变量,该变量仅创建一次并不能保证。如果方法被不同的线程调用,那么可以为每个线程多次创建一次,就像许多线程所称的那样。它应该不会与类的静态成员混淆,该类在程序启动之前创建一次。本地静态变量的线程安全性取决于C++的特定实现。有用的链接:Are function static variables thread-safe in GCC?

希望它有帮助。