Bootstrap

通用的单例模式实例

为什么使用单例模式

使用Head First这本书当中的一段有趣的对话来说明为什么使用单例模式。
开发人员:这有什么用处?

大师:有一些对象其实我们只需要一个,比方说:线程池(threadpool)、缓存(catch)、对话框、处理偏好设置和注册表(registry)的对象、日志对象,充当打印机、显卡等设备的驱动程序的对象。事实上,这类对象只能有一个实例,如果制造出多个实例,就会导致许多问题产生,例如:程序的行为异常、资源使用过量,或者是不一致的结果。

开发人员:好吧!或许的确有一些类应该只存在一个实例,但这需要花整个章节的篇幅来说明吗?难道不能靠程序员之间的约定或是利用全局变量做到?你知道的,例如Java的静态变量就可以做到。

大师:许多时候,的确通过程序员之间的约定就可以办到。但如果有更好的做法,大家都应该乐意接受。别忘了,就跟其它的模式一样,单件模式是经得起时间考验的方法,可以确保只有一个实例会被创建。单件模式也给了我们一个全局的访问点,和全局变量一样方便,有没有全局变量的缺点。

开发人员:什么缺点?

大师:举例来说,如果将对象赋值给一个全局变量,那么你必须在程序一开始就创建好对象(这其实和实现有关。有些JVM的实现是:在用到的时候才创建对象),对吧?万一这个对象非常耗资源,而程序在这次的执行过程中又一直没用到它,不就形成浪费了吗?稍后你会看到,利用单件模式,我们可以在需要时才创建对象。

开发人员:我还是觉得这没有什么困难的。

大师:利用静态类变量、静态方法和适当的访问修饰符(access modifier),你的确可以做到这一点。但是,不管使用哪一种方法,能够了解单件的运作方式仍然是很有趣的事。单件模式听起来简单,要做得对可不简单。不信问问你自己:要如何保证一个对象只能被实例化一次?答案可不是三言两语就能说得完的,是不是?

什么是单例模式?

在Head First这本书当中,是这样来定义单例模式的:
单例模式:确保一个类只有一个实例,并提供全局访问点。

单例模式要点

还是借鉴Head First这本书。
- 单件模式确保程序中一个类最多只有一个实例。
- 单件模式也提供访问这个实例的全局点。
- 在Java中实现单件模式需要私有的构造器,一个静态方法和一个静态变量。
- 确定在性能和资源上的限制,然后小心地选择适当的方案来实现单件,以解决多线程的问题(我们必须认定所有的程序都是多线程的)。
- 如果不是采用第五版的Java 2,双重检查枷锁实现会失效。
- 小心,你如果使用多个类加载器,可能导致单件失效而产生多个实例。
- 如果使用JVM 1.2 或之前的版本,你必须建立单件注册表,以免垃圾收集器将单件回收。

单例模式代码

虽然我们前面都是用的Head First这本书来介绍单例模式相关内容,但是代码我们用的是一个C++类库中的一个部分代码,它是acl(advanced C/C++ library)中的一部分代码(可以说是九牛一毛)。
不过这个方式有一个缺点,就是无论在程序运行过程中是否使用这个单例对象,只要定义了,那么就需要初始化,这样就有可能占用多余的系统资源,这个我们先不考虑。
优点:可以对任何类型的对象进行单例化,因为使用到了模板。
总共用到了3个文件分别是:

noncopyable.hpp

#pragma once
#include "acl_cpp_define.hpp"

namespace acl {

class ACL_CPP_API noncopyable
{
protected:
    noncopyable() {}
    ~noncopyable() {}
private:
    noncopyable( const noncopyable& );
    const noncopyable& operator=( const noncopyable& );
};

}  // namespace acl

acl_cpp_define.hpp

#pragma once

#ifdef ACL_CPP_LIB
# ifndef ACL_CPP_API
#  define ACL_CPP_API
# endif
#elif defined(ACL_CPP_DLL) || defined(_WINDLL)
# if defined(ACL_CPP_EXPORTS) || defined(acl_cpp_EXPORTS)
#  ifndef ACL_CPP_API
#   define ACL_CPP_API __declspec(dllexport)
#  endif
# elif !defined(ACL_CPP_API)
#  define ACL_CPP_API __declspec(dllimport)
# endif
#elif !defined(ACL_CPP_API)
# define ACL_CPP_API
#endif

/*
#ifndef ACL_CPP_TPL
# ifdef ACL_CPP_DLL
#  ifdef ACL_CPP_EXPORTS
#   define ACL_CPP_TPL __declspec(dllexport)
#  else
#   define ACL_CPP_TPL
#  endif
# else
#  define ACL_CPP_TPL
# endif
#endif
*/

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#ifdef  _MSC_VER
# pragma warning(disable:4251)
//# if !defined(VC2003) && !defined(VC6)
//extern "C" { FILE _iob[3] = {__iob_func()[0], __iob_func()[1], __iob_func()[2]}; }
//extern "C" { FILE _iob[3]; }
//# endif
# ifndef    HAS_SSIZE_T
# define    HAS_SSIZE_T
typedef long ssize_t;
# endif
# if(_MSC_VER >= 1300)
#  include <winsock2.h>
#  include <mswsock.h>
# else
#  include <winsock.h>
# endif
#else
# ifdef HAVE_MEMCACHED
#  undef    HAVE_MEMCACHED
# endif
#endif

#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
#define ACL_CPP_PRINTF(format_idx, arg_idx) \
    __attribute__((__format__ (__printf__, (format_idx), (arg_idx))))
#define ACL_CPP_SCANF(format_idx, arg_idx) \
    __attribute__((__format__ (__scanf__, (format_idx), (arg_idx))))
#define ACL_CPP_NORETURN __attribute__((__noreturn__))
#define ACL_CPP_UNUSED __attribute__((__unused__))
#else
#define ACL_CPP_PRINTF(format_idx, arg_idx)
#define ACL_CPP_SCANF
#define ACL_CPP_NORETURN
#define ACL_CPP_UNUSED
#endif  // __GNUC__

#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
#define ACL_CPP_DEPRECATED __attribute__((__deprecated__))
#elif   defined(_MSC_VER) && (_MSC_VER >= 1300)
#define ACL_CPP_DEPRECATED __declspec(deprecated)
#else
#define ACL_CPP_DEPRECATED
#endif  // __GNUC__

#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
#define ACL_CPP_DEPRECATED_FOR(f) __attribute__((deprecated("Use " #f " instead")))
#elif   defined(_MSC_FULL_VER) && (_MSC_FULL_VER > 140050320)
#define ACL_CPP_DEPRECATED_FOR(f) __declspec(deprecated("is deprecated. Use '" #f "' instead"))
#else
#define ACL_CPP_DEPRECATED_FOR(f)   ACL_CPP_DEPRECATED
#endif // __GNUC__

#if defined(__GNUC__) && (__GNUC__ > 6 ||(__GNUC__ == 6 && __GNUC_MINOR__ >= 0))
# ifndef   ACL_USE_CPP11
#  define  ACL_USE_CPP11
# endif
#elif   defined(_MSC_VER) && (_MSC_VER >= 1900)
# ifndef   ACL_USE_CPP11
#  define  ACL_USE_CPP11
# endif
#endif // __GNUC__

singleton.hpp

#pragma once
#include "acl_cpp_define.hpp"
#include <assert.h>
#include "noncopyable.hpp"

//  singleton.hpp
//
// Copyright David Abrahams 2006. Original version
//
// Copyright Robert Ramey 2007.  Changes made to permit
// application throughout the serialization library.
//
// Distributed under the Boost
// Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// The intention here is to define a template which will convert
// any class into a singleton with the following features:
//
// a) initialized before first use.
// b) thread-safe for const access to the class
// c) non-locking
//
// In order to do this,
// a) Initialize dynamically when used.
// b) Require that all singletons be initialized before main
// is called or any entry point into the shared library is invoked.
// This guarentees no race condition for initialization.
// In debug mode, we assert that no non-const functions are called
// after main is invoked.

namespace acl {

#if defined(_WIN32) || defined(_WIN64)
#  pragma warning(push)
#  pragma warning(disable : 4511 4512)
#endif

//////////////////////////////////////////////////////////////////////
// Provides a dynamically-initialized (singleton) instance of T in a
// way that avoids LNK1179 on vc6.  See http://tinyurl.com/ljdp8 or
// http://lists.boost.org/Archives/boost/2006/05/105286.php for
// details.
//

// singletons created by this code are guarenteed to be unique
// within the executable or shared library which creates them.
// This is sufficient and in fact ideal for the serialization library.
// The singleton is created when the module is loaded and destroyed
// when the module is unloaded.

// This base class has two functions.

// First it provides a module handle for each singleton indicating
// the executable or shared library in which it was created. This
// turns out to be necessary and sufficient to implement the tables
// used by serialization library.

// Second, it provides a mechanism to detect when a non-const function
// is called after initialization.

// make a singleton to lock/unlock all singletons for alteration.
// The intent is that all singletons created/used by this code
// are to be initialized before main is called. A test program
// can lock all the singletons when main is entereed.  This any
// attempt to retieve a mutable instances while locked will
// generate a assertion if compiled for debug.

class singleton_module : public noncopyable
{
public:
    static void lock()
    {
        get_lock() = true;
    }

    static void unlock()
    {
        get_lock() = false;
    }

    static bool is_locked() {
        return get_lock();
    }
private:
    static bool& get_lock()
    {
        static bool lock_ = false;
        return lock_;
    }
};

template<class T>
class singleton_wrapper : public T
{
public:
    static bool destroyed_;
    ~singleton_wrapper()
    {
        destroyed_ = true;
    }
};

template<class T>
bool singleton_wrapper< T >::destroyed_ = false;

/**
 * 单例模板类,用VC2010或GCC编译时,单例对象在 main 函数之前被执行,
 * 所以它是线程安全的;但在 VC2003 编译成 release 版本时且打开了优化
 * 开关,则有可能是线程不安全的,此时不能保证单例对象的构造函数在
 * main 之前执行.
 * 使用举例如下:
 * class singleton_test : public acl::singleton<singlegon_test>
 * {
 * public:
 *   singleton_test() {}
 *   ~singleton_test() {}
 *   singleton_test& init() { return *this; }
 * };

 * int main()
 * {
 *   singleton_test& test = singleton_test::get_instance();
 *   test.init();
 *   ...
 *   return 0;
 * }
 */
template <class T>
class singleton : public singleton_module
{
public:
    static T& get_instance()
    {
        static singleton_wrapper< T > t;
        // refer to instance, causing it to be instantiated (and
        // initialized at startup on working compilers)
        assert(!singleton_wrapper< T >::destroyed_);
        use(instance_);
        return static_cast<T &>(t);
    }

    static bool is_destroyed()
    {
        return singleton_wrapper< T >::destroyed_;
    }

private:
    static T& instance_;
    // include this to provoke instantiation at pre-execution time
    static void use(T const &) {}
};

template<class T>
T& singleton< T >::instance_ = singleton< T >::get_instance();

//////////////////////////////////////////////////////////////////////////

/**
 * 上面的实现在 VC2003 的 release 编译时如果打开了优化开关,则不能保证单例
 * 的构造函数先于 main 执行,如果是在 VC2003 下编译单例程序且在多个线程下
 * 都用单例对象时,建议使用如下的单例模板类,示例如下:
 * class singleton_test
 * {
 * public:
 *   singleton_test() {}
 *   ~singleton_test() {}
 *   singleton_test& init() { return *this; }
 * };

 * int main()
 * {
 *   singleton_test& test = acl::singleton2<singleton_test>::get_instance();
 *   test.init();
 *   ...
 *   return 0;
 * }
 * 
 */
template <typename T>
struct singleton2
{
private:
    struct object_creator
    {
        object_creator() { singleton2<T>::get_instance(); }
        inline void do_nothing() const {};
    };
    static object_creator create_object;

public:
    typedef T object_type;
    static object_type & get_instance()
    {
        static object_type obj;
        create_object.do_nothing();
        return obj;
    }
};

template <typename T>
typename singleton2<T>::object_creator singleton2<T>::create_object;

#if defined(_WIN32) || defined(_WIN64)
#pragma warning(pop)
#endif

} // namespace acl

以上就是构成实现单例模式的3个文件及内容。
下面是测试文件
TestSingleton.cpp

// TestSingleton.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
#include "singleton.hpp"


class MyTestClass
{
public:
    MyTestClass()
    {
        m_iCount = 0;
        std::cout << "MyTestClass()" << std::endl;
    }
    ~MyTestClass()
    {
        std::cout << "~MyTestClass()" << std::endl;
    }

public:
    void AddOne()
    {
        ++m_iCount;
    }
    void SubOne()
    {
        --m_iCount;
    }

    void Print() const
    {
        std::cout << "m_iCount: " << m_iCount << std::endl;
    }


private:
    int m_iCount;
};


int main()
{
    MyTestClass& my_test_class1 = acl::singleton2<MyTestClass>::get_instance();
    my_test_class1.Print();
    my_test_class1.AddOne();
    my_test_class1.Print();

    MyTestClass& my_test_class2 = acl::singleton2<MyTestClass>::get_instance();
    my_test_class2.Print();
    my_test_class2.AddOne();
    my_test_class2.Print();

    MyTestClass& my_test_class3 = acl::singleton2<MyTestClass>::get_instance();
    my_test_class3.Print();
    my_test_class3.AddOne();
    my_test_class3.Print();

    system("pause");
    return 0;
}

备注
下面是vs工程的结构
这里写图片描述
运行效果:
这里写图片描述

是不是感觉很酷???

内容如有错误,欢迎大家批评指正!

;