1. 程式人生 > >通用的單例模式例項

通用的單例模式例項

為什麼使用單例模式

使用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工程的結構
這裡寫圖片描述
執行效果:
這裡寫圖片描述

是不是感覺很酷???

內容如有錯誤,歡迎大家批評指正!

相關推薦

通用模式例項

為什麼使用單例模式 使用Head First這本書當中的一段有趣的對話來說明為什麼使用單例模式。 開發人員:這有什麼用處? 大師:有一些物件其實我們只需要一個,比方說:執行緒池(threadpool)、快取(catch)、對話方塊、處理偏好設定和登錄檔(r

通用模式客戶端初始化

HR span except util rop 16px exce div 參數 1 package util; 2 import org.web3j.protocol.geth.Geth; 3 import org.web3j.protocol.http.Http

php模式例項

<?php class Single{//單例模式:一個類只能創建出一個物件(節約記憶體)//三私一公://三私:私有靜態屬性,私有構造方法,私有克隆方法//一公:公共靜態方法//屬性priv

PHP 模式例項與解析

一、什麼是單例模式? 1、含義 作為物件的建立模式,單例模式確保某一個類只有一個例項,而且自行例項化並向整個系統全域性地提供這個例項。它不會建立例項副本,而是會向單例類內部儲存的例項返回一個引用。 2、單例模式的三個要點: (1). 需要一個儲存類

Qt實用技巧:設計模式模式,唯一例項通用模板

需求         Qt常需要一個類,全域性呼叫,是設計模式中的單例模式。 單例模式         單例模式,是一種常用的軟體設計模式。在它的核心結構中只包含一個被稱為單例的特殊類。通過單例模式可以保證系統中,應用該模式的類一個類只有一個例項。即一個類只有一個物

模式通用

csharp esp brush sys instance type spa space create namespace System { /// <summary> /// 為指定的實例創建有線程安全的單例模式。實例必須有一個公開的,無參數

設計模式模式【內附物件例項化幾種方式、實現執行緒安全幾種方式】

繼續來複習常用的設計模式-單例模式,順便回憶一下執行緒安全的幾種實現方式。 一、什麼是單例模式 單例模式,簡單常用的一種設計模式,也很好的體現了程式碼控制物件在記憶體數量的一種方式,主要分2種實現方式: ①餓漢式,執行緒安全 ②懶漢式,執行緒不安全(新增鎖機制,可以實現執行緒安全)

設計模式----模式 【含例項

單例模式,非常常見的一種設計模式。 需求 一個類提供訪問該類物件的唯一方式,且全域性中有且僅有唯一一個該類的例項。 實現方式 1.建構函式private,類外不可建立類例項 2.提供訪問類例項的介面getInstance 3.建立static private的類物件

模式加鎖與不加鎖例項C++

1 教科書裡的單例模式   我們都很清楚一個簡單的單例模式該怎樣去實現:建構函式宣告為private或protect防止被外部函式例項化,內部儲存一個private static的類指標儲存唯一的例項,例項的動作由一個public的類方法代勞,該方法也返回單例類唯一的例項。

模式之懶漢式餓漢式的原理和例項

在java面試題當中,我們經常會遇到單例模式的懶漢式和餓漢式的java筆試題,甚至面試官會問你在開發中,是否用上了這兩種開發模式,用在了哪些例子當中。想了解我們在開發中是否用上了這個,需要我們對這種兩種開發模式的原理有所瞭解。 懶漢式就是當用戶需要用到這個例項的時候,才會加

全域性變數誤用導致模式中的多次銷燬例項產生coredump

最近遇到一個問題,產生了coredump, 用gdb看也沒看出真正原因,合作方同事提醒才看出來。 模擬了一下出錯場景,程式碼如下:   class Person{ private: int *m_data; static Person *

模式(一個類只能建立一個例項

三個步驟:①通過new一個靜態變數                   private static Single  single=new Single1();                   ②構造方法私有化                    private Si

深入理解模式——只有一個例項

目錄: 前言 初遇設計模式在上個寒假,當時把每個設計模式過了一遍,對設計模式有了一個最初級的瞭解。這個學期借了幾本設計模式的書籍看,聽了老師的設計模式課,對設計模式算是有個更進一步的認識。後面可能會不定期更新一下自己對於設計模式的理解。每個設計模式看似很簡單,

Java技術_每天掌握一種設計模式(002)_使用場景及簡單例項(建立型:模式

1.模式描述 一個類有且僅有一個例項,並且自行例項化並向整個系統提供。 2.模式作用 保證某個類在系統中只有一個例項物件,對於特殊需求來說非常必要。 限制了例項個數有利於GC的回收。

實現一個只能例項化一次的類 即 模式(Singleton)

單例模式的要點有三個: 某個類只能有一個例項; 它必須自行建立這個例項; 它必須自行向整個系統提供這個例項。 從具體實現角度來說,就是以下三點: 單例模式的類只提供私有的建構函式 類定義中含有一個該類的靜態私有物件 該類提供了一個靜態的公有的函式

設計模式例項(Lua)筆記之三(Singleton模式

1.描述: 這個模式是很有意思,而且比較簡單,但是我還是要說因為它使用的是如此的廣泛,如此的有人緣,單例就是單一、獨苗的意思,那什麼是獨一份呢?你的思維是獨一份,除此之外還有什麼不能山寨的呢?我們舉個比較難複製的物件:皇帝。    中國的歷史上很少出現兩個皇帝並存的時期,是有

能否寫一個模式,並且保證例項的唯一性?

這算是Java一個比較核心的問題了,面試官期望你能知道在寫單例模式時應該對例項的初始化與否進行雙重檢查。記住對例項的宣告使用Volatile關鍵字,以保證單例模式是執行緒安全的。下面是一段示例,展示瞭如何用一種執行緒安全的方式實現了單例模式: public class

模式---餓漢式(類初始化的時候例項化)

 /**  *  */ package cn.thcic; /**  * 餓漢式(類初始化的時候例項化)單例模式  *  * by Zhiwang Zhang on 2014年7月18日  */ public class Test102 {  // 私有的靜態的本類

js模式的es5實現和es6實現,以及通用惰性實現

單例模式 es5實現 es6實現 單例模式 一開始不建立例項物件,當第一次使用時才建立 用一個變數標誌當前是否已經為某個類建立過物件,如果已建立則在下次獲取時返回之前建立的例項

Java設定模式_建立型_模式_只存在一個例項

1.餓漢式   單例設計模式的寫法存在很多種,常用的寫法可分為懶漢式、餓漢式和登記式。   餓漢式是在類被載入時就已經被例項化,但是對於大的物件,例項化會很消耗資源,因此對於大物件不建議通過該種方式實現單例模式。 public class Sing