Singleton instance declared as static variable of

2020-01-24 02:07发布

I've seen implementations of Singleton patterns where instance variable was declared as static variable in GetInstance method. Like this:

SomeBaseClass &SomeClass::GetInstance()
{
   static SomeClass instance;
   return instance;
}

I see following positive sides of this approach:

  • The code is simpler, because it's compiler who responsible for creating this object only when GetInstance called for the first time.
  • The code is safer, because there is no other way to get reference to instance, but with GetInstance method and there is no other way to change instance, but inside GetInstance method.

What are the negative sides of this approach (except that this is not very OOP-ish) ? Is this thread-safe?

4条回答
2楼-- · 2020-01-24 02:46

According to specs this should also work in VC++. Anyone know if it does?

Just add keyword volatile. The visual c++ compiler should then generate mutexes if the doc on msdn is correct.

SomeBaseClass &SomeClass::GetInstance()
{
   static volatile SomeClass instance;
   return instance;
}
查看更多
聊天终结者
3楼-- · 2020-01-24 02:49

It shares all of the common failings of Singleton implementations, namely:

  • It is untestable
  • It is not thread safe (this is trivial enough to see if you imagine two threads entering the function at the same time)
  • It is a memory leak

I recommend never using Singleton in any production code.

查看更多
够拽才男人
4楼-- · 2020-01-24 02:50

It is not thread safe as shown. The C++ language is silent on threads so you have no inherent guarantees from the language. You will have to use platform synchronization primitives, e.g. Win32 ::EnterCriticalSection(), to protect access.

Your particular approach would be problematic b/c the compiler will insert some (non-thread safe) code to initialize the static instance on first invocation, most likely it will be before the function body begins execution (and hence before any synchronization can be invoked.)

Using a global/static member pointer to SomeClass and then initializing within a synchronized block would prove less problematic to implement.

#include <boost/shared_ptr.hpp>

namespace
{
  //Could be implemented as private member of SomeClass instead..
  boost::shared_ptr<SomeClass> g_instance;
}

SomeBaseClass &SomeClass::GetInstance()
{
   //Synchronize me e.g. ::EnterCriticalSection()
   if(g_instance == NULL)
     g_instance = boost::shared_ptr<SomeClass>(new SomeClass());
   //Unsynchronize me e.g. :::LeaveCriticalSection();
   return *g_instance;
}

I haven't compiled this so it's for illustrative purposes only. It also relies on the boost library to obtain the same lifetime (or there about) as your original example. You can also use std::tr1 (C++0x).

查看更多
smile是对你的礼貌
5楼-- · 2020-01-24 02:57

In C++11 it is thread safe:

§6.7 [stmt.dcl] p4 If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.

In C++03:

  • Under g++ it is thread safe.
    But this is because g++ explicitly adds code to guarantee it.

One problem is that if you have two singletons and they try and use each other during construction and destruction.

Read this: Finding C++ static initialization order problems

A variation on this problem is if the singleton is accessed from the destructor of a global variable. In this situation the singleton has definitely been destroyed, but the get method will still return a reference to the destroyed object.

There are ways around this but they are messy and not worth doing. Just don't access a singleton from the destructor of a global variable.

A Safer definition but ugly:
I am sure you can add some appropriate macros to tidy this up

SomeBaseClass &SomeClass::GetInstance()
{
#ifdef _WIN32 
Start Critical Section Here
#elif  defined(__GNUC__) && (__GNUC__ > 3)
// You are OK
#else
#error Add Critical Section for your platform
#endif

    static SomeClass instance;

#ifdef _WIN32
END Critical Section Here
#endif 

    return instance;
}
查看更多
登录 后发表回答