C ++中的单身人士(至少之前的C ++ 11 AFAIK)可能是一场噩梦。整个静态初始化顺序惨败。但是boost :: call_once似乎提供了一种实现单例的强大方法。我试图想出一个易于使用的成语,我想分享一些批评意见,希望我没有完全愚蠢:)
// Usage:
// You can make anything with a public or protected ctor a singleton.
//
// class A
// {
// public:
// ~A();
// public:
// // Optional, shorter to type
// static A& Instance() { return Singleton<A>::Instance(); }
// void foo();
//
// protected:
// explicit A(); // Can't be private, but can be public, protected is recommended.
// };
//
// Singleton<A>::Instance().foo();
// A::Instance().foo();
//
template< class T >
class Singleton : public T // inerits from T so we can access the protected constructor.
{
public:
virtual ~Singleton() {}
public:
static T& Instance();
private:
static boost::once_flag s_onceFlag;
// We use a raw pointer to avoid dynamic initialisation. If something that
// has a constructor (eg, shared_ptr ) then the dynamically initialised ctor
// may get called after the call once function is called (if the call once function
// is statically invoked before main). Then the instance pointer will be overwritten.
// Using a zero initialised pointer avoids this problem.
static T* s_instance;
private:
static void Init();
private:
explicit Singleton() {} // Used only to allow access to the protected ctor in base.
};
template< class T >
boost::once_flag Singleton<T>::s_onceFlag = BOOST_ONCE_INIT; // zero initialised variable, no order o initialisation shananigans
template< class T >
T* Singleton<T>::s_instance = 0;
template< class T >
void Singleton<T>::Init()
{
static Singleton<T> instance; // static local variable is thread safe since this function is called once only.
s_instance = &instance;
}
template< class T >
T& Singleton<T>::Instance()
{
boost::call_once( s_onceFlag, Init );
return *s_instance;
}
答案 0 :(得分:3)
公平地说,我希望call_once
是一个特定于线程的特定版本的东西,只需要一个函数本地静态初始化器就可以在c ++ 11中使用:
template< class T >
T& Singleton<T>::Instance()
{
static bool const init = [] () -> bool { Init(); return true; }();
return *s_instance;
}
保证初始化程序不会竞争(在语言规范中)。
最糟糕的情况是初始化程序抛出时:异常将从Instance()
方法传播出来,但下次会尝试再次初始化,因为规范认为变量没有被初始化&#34; 34;初始化表达式抛出时。
如果事实上你需要&#34;需要&#34;一个线程感知的单例(例如每个线程的实例),那么你可能需要thread_local
关键字,其语义基本相同。