boost中的单例模式(singleton)

coolshell上有篇文章将单例,讲的已经很好了,最近看了boost的实现,感觉更有一些体会。
原文使用的是java来进行讲解,涉及到了并发的场景。但我这里要解释的,是c++中的单例模式,当然,是通过学习boost的实现。 (boost 1.57.0)

boost中有一些分散的单例实现,能够独立摘出来用的主要有以下四个:

  • boost/container/detail/singleton.hpp
  • boost/serialization/singleton.hpp
  • boost/thread/detail/singleton.hpp
  • boost/pool/singleton_pool.hpp
    尤其以前两个为主。

1. boost/container/detail/singleton.hpp

代码如下:

// T must be: no-throw default constructible and no-throw destructible
template <typename T>
struct singleton_default
{
  private:
    struct object_creator
    {
      // This constructor does nothing more than ensure that instance()
      //  is called before main() begins, thus creating the static
      //  T object before multithreading race issues can come up.
      object_creator() { singleton_default<T>::instance(); }
      inline void do_nothing() const { }
    };
    static object_creator create_object;
 
    singleton_default();
 
  public:
    typedef T object_type;
 
    // If, at any point (in user code), singleton_default<T>::instance()
    //  is called, then the following function is instantiated.
    static object_type & instance()
    {
      // This is the object that we return a reference to.
      // It is guaranteed to be created before main() begins because of
      //  the next line.
      static object_type obj;
 
      // The following line does nothing else than force the instantiation
      //  of singleton_default<T>::create_object, whose constructor is
      //  called before main() begins.
      create_object.do_nothing();
 
      return obj;
    }
};
template <typename T>
typename singleton_default<T>::object_creator
singleton_default<T>::create_object;

其实注释已经说得很清楚了,这个实现使用了一个struct object_creator来作为类的static成员变量,单例的实体是obj,并不暴露出来,只是作为类的成员函数的static变量(local static对象)。通过create_object在main之前被执行构造来保证单例是在main之前被构造好。
这个单例是利用main调用之前,程序只有一个线程,来保证单例在多线程下的唯一性。

其中最迷惑人的也就就是create_object.do_nothing(); 这么用的具体原因,可以参考C++标准中的3.6.2 Initialization of non-local variables:

  1. It is implementation-defined whether the dynamic initialization of a non-local variable with static storage
    duration is done before the first statement of main...

2. boost/serialization/singleton.hpp

代码如下:

class singleton_module : 
    public boost::noncopyable
{
private:
    static bool & get_lock(){
        static bool lock = false;
        return lock;
    }
public:
//    static const void * get_module_handle(){
//        return static_cast<const void *>(get_module_handle);
//    }
    static void lock(){
        get_lock() = true;
    }
    static void unlock(){
        get_lock() = false;
    }
    static bool is_locked() {
        return get_lock();
    }
};
 
namespace detail {
 
template<class T>
class singleton_wrapper : public T
{
public:
    static bool m_is_destroyed;
    ~singleton_wrapper(){
        m_is_destroyed = true;
    }
};
 
template<class T>
bool detail::singleton_wrapper< T >::m_is_destroyed = false;
 
} // detail
 
template <class T>
class singleton : public singleton_module
{
private:
    BOOST_DLLEXPORT static T & instance;
    // include this to provoke instantiation at pre-execution time
    static void use(T const &) {}
    BOOST_DLLEXPORT static T & get_instance() {
        static detail::singleton_wrapper< T > t;
        // refer to instance, causing it to be instantiated (and
        // initialized at startup on working compilers)
        BOOST_ASSERT(! detail::singleton_wrapper< T >::m_is_destroyed);
        use(instance);
        return static_cast<T &>(t);
    }
public:
    BOOST_DLLEXPORT static T & get_mutable_instance(){
        BOOST_ASSERT(! is_locked());
        return get_instance();
    }
    BOOST_DLLEXPORT static const T & get_const_instance(){
        return get_instance();
    }
    BOOST_DLLEXPORT static bool is_destroyed(){
        return detail::singleton_wrapper< T >::m_is_destroyed;
    }
};
 
template<class T>
BOOST_DLLEXPORT T & singleton< T >::instance = singleton< T >::get_instance();

这个实现和detail中的类似,同样通过static变量来完成单例的第一次调用,来保证在main之前构造好,同样使用了一个use来显示的调用static成员变量,触发3.6.2 Initialization of non-local variables。唯一不同的是单例的接口支持了mutable,但是看不出来实际中有什么作用,注释中说了,在debug中可以通过设置lock来测试修改全体单例的地方,可能这个就是用处吧。

3. boost/thread/detail/singleton.hpp

代码:

// class singleton has the same goal as all singletons: create one instance of
// a class on demand, then dish it out as requested.
 
template <class T>
class singleton : private T
{
private:
    singleton();
    ~singleton();
 
public:
    static T &instance();
};
 
 
template <class T>
inline singleton<T>::singleton()
{
    /* no-op */
}
 
template <class T>
inline singleton<T>::~singleton()
{
    /* no-op */
}
 
template <class T>
/*static*/ T &singleton<T>::instance()
{
    // function-local static to force this to work correctly at static
    // initialization time.
    static singleton<T> s_oT;
    return(s_oT);
}

这个thread中的实现就清晰的多,只不过单例是在调用时第一次访问,而不是在main之前,这对于多线程程序来说,要格外注意的。
不过在c++11后,要求编译器保证内部静态变量的线程安全性,因此在支持c++11的编译器中这种方法可以产生线程安全的单例模式,然而在不支持c++11的编译器中,这种方法无法得到保证。

4. boost/pool/singleton_pool.hpp

代码:

template <typename Tag,
    unsigned RequestedSize,
    typename UserAllocator,
    typename Mutex,
    unsigned NextSize,
    unsigned MaxSize >
class singleton_pool
{
  public:
    typedef Tag tag; /*!< The Tag template parameter uniquely
                     identifies this pool and allows
      different unbounded sets of singleton pools to exist.
      For example, the pool allocators use two tag classes to ensure that the
      two different allocator types never share the same underlying singleton pool.
      Tag is never actually used by singleton_pool.
    */
    typedef Mutex mutex; //!< The type of mutex used to synchonise access to this pool (default <tt>details::pool::default_mutex</tt>).
    typedef UserAllocator user_allocator; //!< The user-allocator used by this pool, default = <tt>default_user_allocator_new_delete</tt>.
    typedef typename pool<UserAllocator>::size_type size_type; //!< size_type of user allocator.
    typedef typename pool<UserAllocator>::difference_type difference_type; //!< difference_type of user allocator.
 
    BOOST_STATIC_CONSTANT(unsigned, requested_size = RequestedSize); //!< The size of each chunk allocated by this pool.
    BOOST_STATIC_CONSTANT(unsigned, next_size = NextSize); //!< The number of chunks to allocate on the first allocation.
 
private:
    singleton_pool();
 
#ifndef BOOST_DOXYGEN
    struct pool_type: public Mutex, public pool<UserAllocator>
    {
      pool_type() : pool<UserAllocator>(RequestedSize, NextSize, MaxSize) {}
    }; //  struct pool_type: Mutex
 
#else
    //
    // This is invoked when we build with Doxygen only:
    //
public:
    static pool<UserAllocator> p; //!< For exposition only!
#endif
 
 
  public:
    static void * malloc BOOST_PREVENT_MACRO_SUBSTITUTION()
    { //! Equivalent to SingletonPool::p.malloc(); synchronized.
      pool_type & p = get_pool();
      details::pool::guard<Mutex> g(p);
      return (p.malloc)();
    }
    static void * ordered_malloc()
    {  //! Equivalent to SingletonPool::p.ordered_malloc(); synchronized.
      pool_type & p = get_pool();
      details::pool::guard<Mutex> g(p);
      return p.ordered_malloc();
    }
    static void * ordered_malloc(const size_type n)
    { //! Equivalent to SingletonPool::p.ordered_malloc(n); synchronized.
      pool_type & p = get_pool();
      details::pool::guard<Mutex> g(p);
      return p.ordered_malloc(n);
    }
    static bool is_from(void * const ptr)
    { //! Equivalent to SingletonPool::p.is_from(chunk); synchronized.
      //! \returns true if chunk is from SingletonPool::is_from(chunk)
      pool_type & p = get_pool();
      details::pool::guard<Mutex> g(p);
      return p.is_from(ptr);
    }
    static void free BOOST_PREVENT_MACRO_SUBSTITUTION(void * const ptr)
    { //! Equivalent to SingletonPool::p.free(chunk); synchronized.
      pool_type & p = get_pool();
      details::pool::guard<Mutex> g(p);
      (p.free)(ptr);
    }
    static void ordered_free(void * const ptr)
    { //! Equivalent to SingletonPool::p.ordered_free(chunk); synchronized.
      pool_type & p = get_pool();
      details::pool::guard<Mutex> g(p);
      p.ordered_free(ptr);
    }
    static void free BOOST_PREVENT_MACRO_SUBSTITUTION(void * const ptr, const size_type n)
    { //! Equivalent to SingletonPool::p.free(chunk, n); synchronized.
      pool_type & p = get_pool();
      details::pool::guard<Mutex> g(p);
      (p.free)(ptr, n);
    }
    static void ordered_free(void * const ptr, const size_type n)
    { //! Equivalent to SingletonPool::p.ordered_free(chunk, n); synchronized.
      pool_type & p = get_pool();
      details::pool::guard<Mutex> g(p);
      p.ordered_free(ptr, n);
    }
    static bool release_memory()
    { //! Equivalent to SingletonPool::p.release_memory(); synchronized.
      pool_type & p = get_pool();
      details::pool::guard<Mutex> g(p);
      return p.release_memory();
    }
    static bool purge_memory()
    { //! Equivalent to SingletonPool::p.purge_memory(); synchronized.
      pool_type & p = get_pool();
      details::pool::guard<Mutex> g(p);
      return p.purge_memory();
    }
 
private:
   typedef boost::aligned_storage<sizeof(pool_type), boost::alignment_of<pool_type>::value> storage_type;
   static storage_type storage;
 
   static pool_type& get_pool()
   {
      static bool f = false;
      if(!f)
      {
         // This code *must* be called before main() starts, 
         // and when only one thread is executing.
         f = true;
         new (&storage) pool_type;
      }
 
      // The following line does nothing else than force the instantiation
      //  of singleton<T>::create_object, whose constructor is
      //  called before main() begins.
      create_object.do_nothing();
 
      return *static_cast<pool_type*>(static_cast<void*>(&storage));
   }
 
   struct object_creator
   {
      object_creator()
      {  // This constructor does nothing more than ensure that instance()
         //  is called before main() begins, thus creating the static
         //  T object before multithreading race issues can come up.
         singleton_pool<Tag, RequestedSize, UserAllocator, Mutex, NextSize, MaxSize>::get_pool();
      }
      inline void do_nothing() const
      {
      }
   };
   static object_creator create_object;
}; // struct singleton_pool
 
template <typename Tag,
    unsigned RequestedSize,
    typename UserAllocator,
    typename Mutex,
    unsigned NextSize,
    unsigned MaxSize >
typename singleton_pool<Tag, RequestedSize, UserAllocator, Mutex, NextSize, MaxSize>::storage_type singleton_pool<Tag, RequestedSize, UserAllocator, Mutex, NextSize, MaxSize>::storage;
 
template <typename Tag,
    unsigned RequestedSize,
    typename UserAllocator,
    typename Mutex,
    unsigned NextSize,
    unsigned MaxSize >
typename singleton_pool<Tag, RequestedSize, UserAllocator, Mutex, NextSize, MaxSize>::object_creator singleton_pool<Tag, RequestedSize, UserAllocator, Mutex, NextSize, MaxSize>::create_object;

这个是在pool中的单例实现,所有的接口都和pool类似,使用也类似,单例的实现类似于detail的实现,包括do_nothing()的使用,显示线程安全和在main之前构造好的,细节参考detail的实现。

看了上面4个单例的实现,基本上1最典型,保证了main之前的调用。但是boost为什么没有一个通用的单例类呢?是因为Are Singletons really that bad?

可以在stackoverflow搜搜这个讨论,可能这也是boost没有一个通用的单例类的原因,已提供的单例类,都是在某个具体的模块中。学习不代表要使用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 229,836评论 6 540
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 99,275评论 3 428
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 177,904评论 0 383
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 63,633评论 1 317
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,368评论 6 410
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 55,736评论 1 328
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 43,740评论 3 446
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 42,919评论 0 289
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,481评论 1 335
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,235评论 3 358
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,427评论 1 374
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 38,968评论 5 363
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,656评论 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 35,055评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,348评论 1 294
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 52,160评论 3 398
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,380评论 2 379

推荐阅读更多精彩内容

  • 前言 本文主要参考 那些年,我们一起写过的“单例模式”。 何为单例模式? 顾名思义,单例模式就是保证一个类仅有一个...
    tandeneck阅读 2,529评论 1 8
  • 简介 单例模式是一种常用的软件设计模式,其定义是单例对象的类只能允许一个实例存在。许多时候整个系统只需要拥有一个的...
    上杉丶零阅读 580评论 0 1
  • 1.单例模式概述 (1)引言 单例模式是应用最广的模式之一,也是23种设计模式中最基本的一个。本文旨在总结通过Ja...
    曹丰斌阅读 2,975评论 6 47
  • 有人说,雨是老天爷流下的眼泪,所以当有雨坠下时,我们会毫无知觉的就在心上蒙上一层淡淡的忧伤,好像我们在陪同老天爷一...
    蒙语阅读 1,289评论 0 0
  • css——层叠样式表 选择器 用于匹配HTML元素有不同的匹配规则多个选择器可以叠加 值得关注的选择器元素选择器 ...
    Love小六六阅读 207评论 0 0