By SmartPtr(http://www.shnenglu.com/SmartPtr/)
對于單件模式, 我想大家應該都不陌生,它可以說是GOF23個設計模式中最簡單,最常用的一個模式了。但看似簡單的東西卻不一定好用,我就在使用時遇到了一個問題。
1 一個簡單的Singleton類
#include<iostream>
class Singleton
{
public:
static Singleton& GetSingleton()
{
static Singleton singleton;
return singleton;
}
void Print()
{
std::cout<<"Singleton Print\n";
}
private:
Singleton::Singleton()
{
std::cout<<"singleton constructor\n";
}
};
這個類提供了一些的功能(函數Print), 并禁止我們創建這個類的對象(構造函數為private), 提供給我們一個靜態函數接口來訪問這個單件對象(GetSingleton),利用靜態變量的特點實現了其單一性。但是, 這個類有問題嗎?
2 問題所在
是的,上面這個簡單的類的確存在問題,而且是一個很嚴重的問題,這個問題讓Singleton類完全失去它存在的意義, 因為它不再唯一!是的, 當我們只在一個模塊中使用這個類時(比如說,一個exe),這個類是沒有問題的。但是, 一個稍微復雜一點的軟件, 為了開發的便捷,提高復用度,降低耦合性等原因,其難免會被分成好幾個模塊。那么假設講我現在有兩個模塊,一個DLL(singleton.dll), 用來提供一些基礎的功能, 一個EXE(test.exe),用來提供真正的軟件邏輯。 我現在singleton.dll中封裝了一個Print的函數間(用類Singleton實現)并暴露出來。
singleton.dll
void Print()
{
Singleton::GetSingleton().Print();
}
并在test.exe中這樣調用:
Test.exe
Singleton::GetSingleton().Print();
Print();
這個時候,我們會發現在調用Singleton::GetSingleton().Print()時會產生一個Singleton對象, 而在調用Print()時, 也會產生一個Singleton對象, 也就是說我們有了兩個Singleton實例, singleton不再是singleton。那么,為什么會這樣呢。
static Singleton& GetSingleton()
{
static Singleton singleton;
return singleton;
}
這個函數應該只會在第一次調用時創建Singleton對象,無論如何, 不應該出現會創建兩次, 調用兩次構造函數的情況。對于靜態變量特性理解沒錯(只在第一次經過時被初始化), 編譯器也沒問題(vc8.0),難道兩次經過該靜態變量是都是第一次? 那么,難道兩次調用的GetSingleton函數并不是同一個函數?讓我們逐一來看:
1) Singleton::GetSingleton().Print()
在Test.exe中直接調用該函數,因為包含的頭文件singleton.h有完整的實現, 在鏈接時會在Test.exe保存一份Singleton::GetSingleton()的實現代碼。
將其標為Singleton::GetSingleton_1();
2) Print();
Print()函數是從singleton.dll中導出而來的,而Print()會調用Singleton::GetSingleton(), 在鏈接模塊singleton.dll時,因為其包含的頭文件有完整的實現, 這個DLL也會保存一份Singleton::GetSingleton()的執行代碼。 我將它標為Singleton::GetSingleton_2(), 雖然我們包含的是同一個頭文件,兩個是相同的函數名字, 但是這個函數在兩個不同的模塊中都存有一份獨立的實現。實際上, 他們已經成為兩個不同的函數了。
看來,兩個函數的確不是同一個函數。
3 如何解決
既然知道了原因,就會有相應的解決方法。既然我們知道有兩份獨立的代碼分別存在于兩個模塊中, 那么我們要做的就是讓它只有一份。最好的結果就是這個函數保存在dll中, 在Test.exe不再存有該函數的執行代碼, 而是調用dll中的那個函數。現在結果很明顯了:將Singleton.h編譯鏈接singleton.dll并將外部需要使用的函數暴露出來。這樣,不管有多少模塊使用到singleton, 我們始終執行singleton.dll中的代碼。
如下:
SINGLETON_API static Singleton& GetSingleton()
{
static Singleton singleton;
return singleton;
}
注:
#ifdef SINGLETON_EXPORTS
#define SINGLETON_API __declspec(dllexport)
#else
#define SINGLETON_API __declspec(dllimport)
#endif
這樣在test.exe中使用該函數時,就不會再產生一個副本了,從而保證了我們的應用程序只有一個singleton實例
posted on 2007-08-28 11:37
SmartPtr 閱讀(1519)
評論(0) 編輯 收藏 引用