使用 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 关键字,其语义基本相同。