C++完美實現Singleton模式

Singleton模式是常用的設計模式之一,但是要實現一個真正實用的設計模式卻也不是件容易的事情。

1.         標準的實現

class Singleton

{

public:

       static Singleton * Instance()

       {

              if( 0== _instance)

              {

                     _instance = new Singleton;

              }

              return _instance;

       }

protected:

       Singleton(void)

       {

       }

       virtual ~Singleton(void)

       {

       }

       static Singleton* _instance;

};

       這是教科書上使用的方法。看起來沒有什麼問題,其實包含很多的問題。下面我們一個一個的解決。

2.         自動垃圾回收

上面的程序必須記住在程序結束的時候,釋放內存。爲了讓它自動的釋放內存,我們引入auto_ptr改變它。

#include <memory>

#include <iostream>

using namespace std;

class Singleton

{

public:

       static Singleton * Instance()

       {

              if( 0== _instance.get())

              {

                     _instance.reset( new Singleton);

              }

              return _instance.get();

       }

protected:

       Singleton(void)

       {

              cout <<"Create Singleton"<<endl;

       }

       virtual ~Singleton(void)

       {

              cout << "Destroy Singleton"<<endl;

       }

       friend class auto_ptr<Singleton>;

       static auto_ptr<Singleton> _instance;

};

//Singleton.cpp

auto_ptr<Singleton> Singleton::_instance;

3.         增加模板

在我的一個工程中,有多個的Singleton類,對Singleton類,我都要實現上面這一切,這讓我覺得煩死了。於是我想到了模板來完成這些重複的工作。

現在我們要添加本文中最吸引人單件實現:

/********************************************************************

    (c) 2003-2005 C2217 Studio

    Module:    Singleton.h

    Author:     Yangjun D.

    Created:    9/3/2005   23:17

    Purpose:    Implement singleton pattern

    History:

*********************************************************************/

#pragma once

 

#include <memory>

using namespace std;

using namespace C2217::Win32;

 

namespace C2217

{

namespace Pattern

{

template <class T>

class Singleton

{

public:

       static inline T* instance();

      

private:

       Singleton(void){}

       ~Singleton(void){}

       Singleton(const Singleton&){}

       Singleton & operator= (const Singleton &){}

 

       static auto_ptr<T> _instance;

};

 

template <class T>

auto_ptr<T> Singleton<T>::_instance;

 

template <class T>

 inline T* Singleton<T>::instance()

{

       if( 0== _instance.get())

       {

              _instance.reset ( new T);

       }

      

       return _instance.get();

}

 

//Class that will implement the singleton mode,

//must use the macro in it's delare file

#define DECLARE_SINGLETON_CLASS( type ) /

       friend class auto_ptr< type >;/

       friend class Singleton< type >;

}

}

 

4.         線程安全

上面的程序可以適應單線程的程序。但是如果把它用到多線程的程序就會發生問題。主要的問題在於同時執行_instance.reset ( new T); 就會同時產生兩個新的對象,然後馬上釋放一個,這跟Singleton模式的本意不符。所以,你需要更加安全的版本:

/********************************************************************

    (c) 2003-2005 C2217 Studio

    Module:    Singleton.h

    Author:     Yangjun D.

    Created:    9/3/2005   23:17

    Purpose:    Implement singleton pattern

    History:

*********************************************************************/

#pragma once

 

#include <memory>

using namespace std;

#include "Interlocked.h"

using namespace C2217::Win32;

 

namespace C2217

{

namespace Pattern

{

template <class T>

class Singleton

{

public:

       static inline T* instance();

      

private:

       Singleton(void){}

       ~Singleton(void){}

       Singleton(const Singleton&){}

       Singleton & operator= (const Singleton &){}

 

       static auto_ptr<T> _instance;

       static CResGuard _rs;

};

 

template <class T>

auto_ptr<T> Singleton<T>::_instance;

 

template <class T>

CResGuard Singleton<T>::_rs;

 

template <class T>

 inline T* Singleton<T>::instance()

{

       if( 0 == _instance.get() )

       {

              CResGuard::CGuard gd(_rs);

              if( 0== _instance.get())

              {

                     _instance.reset ( new T);

              }

       }

       return _instance.get();

}

 

//Class that will implement the singleton mode,

//must use the macro in it's delare file

#define DECLARE_SINGLETON_CLASS( type ) /

       friend class auto_ptr< type >;/

       friend class Singleton< type >;

}

}

       CresGuard 類主要的功能是線程訪問同步,代碼如下:

/******************************************************************************

Module:  Interlocked.h

Notices: Copyright (c) 2000 Jeffrey Richter

******************************************************************************/

 

 

#pragma once

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

 

// Instances of this class will be accessed by multiple threads. So,

// all members of this class (except the constructor and destructor)

// must be thread-safe.

class CResGuard {

public:

   CResGuard()  { m_lGrdCnt = 0; InitializeCriticalSection(&m_cs); }

   ~CResGuard() { DeleteCriticalSection(&m_cs); }

 

   // IsGuarded is used for debugging

   BOOL IsGuarded() const { return(m_lGrdCnt > 0); }

 

public:

   class CGuard {

   public:

      CGuard(CResGuard& rg) : m_rg(rg) { m_rg.Guard(); };

      ~CGuard() { m_rg.Unguard(); }

 

   private:

      CResGuard& m_rg;

   };

 

private:

   void Guard()   { EnterCriticalSection(&m_cs); m_lGrdCnt++; }

   void Unguard() { m_lGrdCnt--; LeaveCriticalSection(&m_cs); }

 

   // Guard/Unguard can only be accessed by the nested CGuard class.

   friend class CResGuard::CGuard;

 

private:

   CRITICAL_SECTION m_cs;

   long m_lGrdCnt;   // # of EnterCriticalSection calls

};

 

 

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

5.         實用方法

比如你有一個需要實現單件模式的類,就應該這樣實現:

#pragma once

#include "singleton.h"

using namespace C2217::Pattern;

 

class ServiceManger

{

public:

       void Run()

       {

       }

private:

       ServiceManger(void)

       {

       }

       virtual ~ServiceManger(void)

       {

       }

       DECLARE_SINGLETON_CLASS(ServiceManger);

};

 

typedef Singleton<ServiceManger> SSManger;

 

在使用的時候很簡單,跟一般的Singleton實現的方法沒有什麼不同。

int _tmain(int argc, _TCHAR* argv[])

{

        SSManger::instance()->Run();

}

 

一個簡單的Singleton模式的實現,可以看到C++語言背後隱藏的豐富的語意,我希望有人能實現一個更好的Singleton讓大家學習。我從一開始實現Singleton類的過程,其實就是我學習C++的過程,越是深入越覺得C++了不起。

 

 

 
posted on 2005-09-20 22:14 天下無雙 閱讀(530) 評論(9)  編輯 收藏 收藏至365Key

評論

似乎可以這樣:
template <class T>
class Singleton
{
public:
static inline T& instance()
{
static T _instance;
return _instance;
}

private:
Singleton(void);
~Singleton(void);
Singleton(const Singleton<T>&);
Singleton<T>& operator= (const Singleton<T> &);
};
  
  

Cool!
比我的實現簡單很多,並且也達到了同樣的效果,不佩服都不行.  
  

mexo 說的實現方式Meyer(Effective c++)提出來的

這種方式並不是沒有多線程問題的  
  

mexo提出的方案的確不錯,但是好象也並不完美(我不是指多線程解決方案),因爲他把模板類的構造函數放在私有段裏了,如果放在protected段裏就好得多,因爲你的類可以從模板類繼承,這樣就不再需要你的那個 typedef Singleton<ServiceManger> SSManger;定義了。示例如下:
template <class T>
class Singleton {
public:
static T& instance() {
static T _instance;
return _instance;
}
protected:
Singleton(void) {}
virtual ~Singleton(void) {}
Singleton(const Singleton<T>&); //不實現
Singleton<T>& operator= (const Singleton<T> &); //不實現
};
--------------------------------
下面是一個需要做爲單例的類,只需從Singleton繼承即可
class Test : public Singleton<Test> {
public:
void foo();
private:
Test();
~Test();
friend class Singleton<Test>;
};
----------------------
這樣,別人在使用的時候,只需要寫
Test::instance().foo();
而再也不需要寫:
Singleton<Test>::instance().foo();
或者
typedef Singleton<Test> STest;
STest::instance().foo();
-----------------------------
  
  

我猜樓主一定沒看過Andrei Alexandrescu寫的Modern C++ Design  
  

我是寫些Singleton的實現, 後來有機會看來你說的那本書, 感覺思想差不多,不謀而和。  
  

Singleton在多線程方面,還漏了一篇文章沒有看:
《Double-Checked Locking,Threads,Compiler Optimizations,and More》(Scott Meyers)

主要意思就是在:_instance.reset ( new T);
在編譯器優化情況下,_instance先設了指針內容,然後再進行構造函數。如果有第二個線程這時候進行訪問,_instance內容爲非空,於是跳過了第一個if( 0 == _instance.get() )。但實際上對象還是沒有構造完整。

實際上我們多是先用
std::auto_ptr<T> _au(new T);
_instance = _au;
  
  

如果已經作了多線程保護,_instance.reset ( new T); 和std::auto_ptr<T> _au(new T);
_instance = _au;
不一樣嗎?  
  

Sorry,
if( 0 == _instance.get() )
{
CResGuard::CGuard gd(_rs);
if( 0== _instance.get())
{
_instance.reset ( new T);
}
如果把CResGuard::CGuard gd(_rs);放到第一句即可解決,但卻帶來了效率問題。這就是DCL要解決的內容
# re: C++完美實現Singleton模式 2005-09-21 00:31 mexo
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章