boost中的单例模式(singleton)

原创 2015年01月04日 12:19:55

coolshell上有篇文章将单例,讲的已经很好了,最近看了boost的实现,感觉更有一些体会。
coolshell的网址:http://coolshell.cn/articles/265.html,推荐,使用的是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:

4. 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之前,这对于多线程程序来说,要格外注意的。

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搜搜这个讨论(http://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons),可能这也是boost没有一个通用的单例类的原因,已提供的单例类,都是在某个具体的模块中。学习不代表要使用。

可能,下一个讨论就是,有哪些情况是单例能够做到而全局变量做不到的呢?在main之前调用一段代码仅一次,有哪些方法?

待续...

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

boost::serialization::singleton单例的使用方式

#include "stdafx.h" #include #include #include // 使用方式1:模板参数方式 class CTest:public boost::noncopy...

使用boost库实现一个单例模式

单例模式是最常使用的设计模式之一,特别在跨文件编程时使用的最多。 #include #include #include template class Singleton: boost::non...

不简单的单例模式Singleton

单例模式,即
  • yockie
  • yockie
  • 2014年11月05日 00:53
  • 3420

(七)boost库之单例类

一、boost.serialzation的单件实现     单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易...

BOOST的Singleton模版详解

首先要说明,这个准确说并不是BOOST的singleton实现,而是BOOST的POOL库的singleton实现。BOOST库中其实有若干个singleton模版,这个只是其中一个。但网上大部分介绍...

内存管理 Boost::singleton_pool

singleton_pool与pool的接口完全一致,可以分配简单数据类型(POD)的内存指针,但它是一个单件,并提供线程安全。 由于目前Boost还未提供标准的单件库,singleton_po...
  • educast
  • educast
  • 2014年03月12日 17:18
  • 3448

每天一题(48) - C++实现Singleton模式

饿汉模式 代码(1) //.h文件 class Singleton { public: static Singleton& GetInstance(); private: Singleton(){...

c++中的.hpp文件

c++中的.hpp文件  hpp,其实质就是将.cpp的实现代码混入.h头文件当中,定义与实现都包含在同一文件,则该类的调用者只需要include该cpp文件即可,无需再 将cpp加...

boost中serialization模块的单体类

// singleton.cpp : 定义控制台应用程序的入口点。 //BOOST实现单体的两种方式 //1.通过boost的pool的singleton实现 //2.通过boost的seria...

Singleton 单例模式 -- 饿汉

/*直接照着boost库的一个比较广泛的单例模式写的,一字不错,boost有很多单例模式可以研究 *最近项目用到一个单例模式,是教科书使得,'懒汉模式',这个是'饿汉模式',优点是在main函数 ...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:boost中的单例模式(singleton)
举报原因:
原因补充:

(最多只允许输入30个字)