class Singleton
{
public:
static Singleton* Instance();
protected:
Singleton();
private:
static Singleton* _instance;
};
相應(yīng)的實(shí)現(xiàn) cpp 文件是:
Singleton* Singleton::_instance;
Singleton* Singleton::Instance()
{
if( _instance == 0){
_instance = new Singleton;
};
return _instance;
}
將構(gòu)造函數(shù)設(shè)計(jì)成 protected 的目的是防止在 class 外面 new ,有人可能會(huì)設(shè)計(jì)成 private ,假如考慮到有可能會(huì)繼續(xù)這個(gè)類的話,還是將構(gòu)造函數(shù)設(shè)計(jì)成 protected 比較好,還需要加一個(gè) virtual 析構(gòu)函數(shù)。為了防止別人復(fù)制 Singleton 對(duì)象:
Singleton* pSingleton = Singleton::Instance();
Singleton s1 = *pSingleton;
Singleton s2 = *pSingleton;
需要將拷貝構(gòu)造(copy constrUCtor)函數(shù)變成 private。
但是這里存在的問題是,什么時(shí)候刪除 Singleton 對(duì)象?按照 C++ 的一個(gè)基本原則,對(duì)象在哪里創(chuàng)建就在哪里銷毀,這里還應(yīng)該放一個(gè) destroy 方法來刪除 Singleton 對(duì)象。假如忘了刪除就比較麻煩。Instance 函數(shù)還存在多線程同時(shí)訪問的加鎖問題。假如把 Instance 函數(shù)開始和結(jié)尾放上加鎖和解鎖,整個(gè)函數(shù)性能會(huì)下降很多。這不是一個(gè)好的設(shè)計(jì)。
有一個(gè)小小的改動(dòng),可以避免忘了刪除 Singleton 對(duì)象帶來內(nèi)存泄露的問題。那就是用 std:auto_ptr 來包含 Singleton 對(duì)象,定義一個(gè)類靜態(tài)auto_ptr成員對(duì)象,在析構(gòu)靜態(tài)的auto_ptr 變量的時(shí)候時(shí)候自動(dòng)刪除 Singleton 對(duì)象。為了不讓用戶 delete Singleton 對(duì)象,需要將析構(gòu)函數(shù)由 public 變成 protected。以下是頭文件 SingletonAutoPtr.h :
#include <memory>
using namespace std;
class CSingletonAutoPtr
{
private:
static auto_ptr<CSingletonAutoPtr> m_auto_ptr;
static CSingletonAutoPtr* m_instance;
protected:
CSingletonAutoPtr();
CSingletonAutoPtr(const CSingletonAutoPtr&);
virtual ~CSingletonAutoPtr();
//allow auto_ptr to delete, using protected ~CSingletonAutoPtr()
friend class auto_ptr<CSingletonAutoPtr>;
public:
static CSingletonAutoPtr* GetInstance();
void Test();
};
對(duì)應(yīng)的 SingletonAutoPtr.cpp 如下:
#include "SingletonAutoPtr.h"
#include <iostream>
//initial static member vars here
CSingletonAutoPtr* CSingletonAutoPtr::m_instance = NULL;
auto_ptr<CSingletonAutoPtr> CSingletonAutoPtr::m_auto_ptr;
/////////////////////////////////////////
// Construction/Destruction
/////////////////////////////////////////
//下列代碼沒有經(jīng)過驗(yàn)證!
CSingletonAutoPtr::CSingletonAutoPtr()
{
cout << "CSingletonAutoPtr::CSingletonAutoPtr()" << endl;
//put single object into auto_ptr object
m_auto_ptr = auto_ptr<CSingletonAutoPtr>(this);
}
CSingletonAutoPtr::~CSingletonAutoPtr()
{
cout << "CSingletonAutoPtr::~CSingletonAutoPtr()" << endl;
}
CSingletonAutoPtr* CSingletonAutoPtr::GetInstance()
{
//begin lock
//....
if(m_instance == NULL)
m_instance = new CSingletonAutoPtr();
//end lock
//...
return m_instance;
}
void CSingletonAutoPtr::Test()
{
cout << "CSingletonAutoPtr::Test()" << endl;
}
調(diào)用方法:
CSingletonAutoPtr* pSingleton = CSingletonAutoPtr::GetInstance();
pSingleton->Test();
寫一個(gè) C++ 中的 Singleton 需要這么費(fèi)勁,大大出乎我們的意料。有很多人從未用過 auto_ptr,而且 std:auto_ptr 本身就并不完美,它是基于對(duì)象所有權(quán)機(jī)制的,相比之下,Apache Log4cxx 中有一個(gè) auto_ptr, 是基于對(duì)象計(jì)數(shù)的,更為好用。只是為了用一個(gè)好的 auto_ptr 而不得不用 log4cxx , 對(duì)于很多項(xiàng)目來說,也不太好。當(dāng)然了,ANSI C++ 的 STL 中 std:auto_ptr 對(duì)于寫上面的例子已經(jīng)足夠用了。
另外一個(gè)思路是,把 GetInstance 函數(shù)設(shè)計(jì)成 static member 可能更好,因?yàn)橐话銇碚f,Singleton 對(duì)象都不大,static member 雖然必須一直占用內(nèi)存,問題不大。這里的析構(gòu)函數(shù)必須設(shè)成 public 了。以下是頭文件 SingleStaticObj.h
class CSingletonStaticObj
{
private:
static CSingletonStaticObj m_instance;
protected:
CSingletonStaticObj();
CSingletonStaticObj(const CSingletonStaticObj&);
public:
virtual ~CSingletonStaticObj(); //must public
static CSingletonStaticObj& GetInstance();
void Test();
}; 對(duì)應(yīng)的 SingleStaticObj.cpp 文件為:#include "SingletonStaticObj.h"
#include <string>
#include <iostream>
using namespace std;
CSingletonStaticObj CSingletonStaticObj::m_instance;CSingletonStaticObj::CSingletonStaticObj()
{
cout << "CSingletonStaticObj::CSingletonStaticObj()" << endl;
}
CSingletonStaticObj::~CSingletonStaticObj()
{
cout << "CSingletonStaticObj::~CSingletonStaticObj()" << endl;
}
CSingletonStaticObj& CSingletonStaticObj::GetInstance()
{
return m_instance;
}
void CSingletonStaticObj::Test()
{
cout << "CSingletonStaticObj::Test()" << endl;
}
調(diào)用方法:
CSingletonStaticObj& singleton = CSingletonAutoPtr::GetInstance();
singleton.Test();
從代碼量來說,似乎使用 static member ref 更為簡(jiǎn)單。我更偏向于用這種方法。
但是,并不是所有情況下面都適合用 static member singleton。比如說,GetInstance 需要?jiǎng)討B(tài)決定返回不同的 instance 的時(shí)候,就不能用。舉例來說,FileSystem::GetInstance, 在 windows 下面運(yùn)行可能需要返回 new WinFileSystem, Linux/Unix 下面運(yùn)行可能需要返回 new LinuxFileSystem,這個(gè)時(shí)候還是需要用上面的 auto_ptr 包含 singleton 指針的方法。