使用 boost::call_once 的 C++ 单例模式
C++ singleton pattern using boost::call_once
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;
}
公平地说,我希望 call_once
是线程特定版本的东西,在 c++11 中已经可以使用函数局部静态初始化程序:
template< class T >
T& Singleton<T>::Instance()
{
static bool const init = [] () -> bool { Init(); return true; }();
return *s_instance;
}
初始化程序保证不会竞争(在语言规范中)。
最坏的情况是初始化器抛出异常:异常将从 Instance()
方法中传播出去,但下一次将尝试再次初始化,因为规范认为变量在 "not been initialized" 时初始化表达式抛出。
如果实际上您 "need" 一个线程感知单例(例如每个线程的实例),那么您可能需要 thread_local
关键字,其语义基本相同。
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;
}
公平地说,我希望
call_once
是线程特定版本的东西,在 c++11 中已经可以使用函数局部静态初始化程序:template< class T > T& Singleton<T>::Instance() { static bool const init = [] () -> bool { Init(); return true; }(); return *s_instance; }
初始化程序保证不会竞争(在语言规范中)。
最坏的情况是初始化器抛出异常:异常将从
Instance()
方法中传播出去,但下一次将尝试再次初始化,因为规范认为变量在 "not been initialized" 时初始化表达式抛出。如果实际上您 "need" 一个线程感知单例(例如每个线程的实例),那么您可能需要
thread_local
关键字,其语义基本相同。