單例模式顧名思義就是隻能生成一個示例。其實在我個人不太成熟的理解上來說,單例模式的作用等同於在所有的成員變量和成員類前面加上一個“static”。所有調用這個類生成的實例都是同一個,因此我認爲等同於將所有的類資源變成靜態資源。
既然要有且僅有生成一個實例,那麼我們需要做的就兩件事。
1.將所有能夠生成類實例的途徑全部堵上,例如構造函數、拷貝構造函數私有化,外部無法訪問也就無法通過他們生成實例了。
2.既然無法在外部生成類的實例,那麼我們就需要想辦法在類的內部生成類的實例,並且能夠被我們所調用。
綜上所訴,就可以得到一個簡單的單例模式的例子了:
#include <iostream>
#define debug
class Single{
private:
Single(){
this->test = 0;
}
Single(const Single&){;}
Single& operator = (const Single&){;}
static Single* instance;
public:
static Single* getInstance();
int test;
};
Single* Single::instance = NULL;
Single* Single::getInstance(){
if(instance == NULL){
instance = new Single();
}
return instance;
}
int main(){
Single* single_1 = Single::getInstance();
Single* single_2 = Single::getInstance();
if(single_1 == single_2)
std::cout << "single_1與single_2的地址一致" << std::endl << std::endl;
#ifdef debug
std::cout << "single_1:" << single_1->test << std::endl;
std::cout << "single_2:" << single_1->test << std::endl << std::endl;
single_1->test = 5;
std::cout << "single_1:" << single_1->test << std::endl;
std::cout << "single_2:" << single_1->test << std::endl << std::endl;
single_2->test = 10;
std::cout << "single_1:" << single_1->test << std::endl;
std::cout << "single_2:" << single_1->test << std::endl << std::endl;
#endif
std::cin.get();
return 0;
}
運行結果:
爲了說明單例模式是成功的,代碼先比較了“兩個實例”的地址,程序輸出結果表明地址是一致的。我們還在下面先後修改了single_1和Single_2實例中test的值,輸出結果表明,sing_1和sing_2中test的值始終保持一致,也證明了僅生成了一個實例。
單例模式中還有一種稱作是餓漢單例模式的分類,餓漢單例模式簡單粗暴,在類內部直接生成一個實例,封堵其餘生成實例的途徑,以下是例子:
#include <iostream>
#define debug
class Single_hunger{
private:
Single_hunger(){
this->test = 0;
}
Single_hunger(const Single_hunger&){;}
Single_hunger& operator = (const Single_hunger&){;}
static Single_hunger* instance;
public:
static Single_hunger* getInstance();
int test;
};
Single_hunger* Single_hunger::instance = new Single_hunger();
Single_hunger* Single_hunger::getInstance(){
return instance;
}
int main(){
Single_hunger* single_1 = Single_hunger::getInstance();
Single_hunger* single_2 = Single_hunger::getInstance();
if(single_1 == single_2)
std::cout << "single_1與single_2的地址一致" << std::endl << std::endl;
#ifdef debug
std::cout << "single_1:" << single_1->test << std::endl;
std::cout << "single_2:" << single_1->test << std::endl << std::endl;
single_1->test = 5;
std::cout << "single_1:" << single_1->test << std::endl;
std::cout << "single_2:" << single_1->test << std::endl << std::endl;
single_2->test = 10;
std::cout << "single_1:" << single_1->test << std::endl;
std::cout << "single_2:" << single_1->test << std::endl << std::endl;
#endif
std::cin.get();
return 0;
}
可以看出,餓漢單例和普通的單例模式的區別在於,普通單例的實例是在getInstance()函數中生成的,當且僅當訪問這個函數的時候纔會生成實例;而餓漢模式是在類的內部實現的,因此一開始就已經存在了這個實例,因此,餓漢模式的調用速度和反應速度更快,但是資源利用效率和系統加載時間上就表現比較差了。
同時,餓漢單例模式無需考慮多線程的影響,如果系統採用了多線程,那麼普通的單例模式就不能滿足系統的需求了。因爲我們要創建實例,在多線程訪問的情況下,我們需要加鎖,這種模式叫懶漢模式,一下是例子:
#include<iostream>
#define debug
class Single_lazy{
private:
Single_lazy(){
this->test = 0;
}
static Single_lazy* instance;
Single_lazy(const Single_lazy&){;}
Single_lazy& operator = (const Single_lazy&){;}
static bool lock;
public:
int test;
static Single_lazy* getInstance();
};
Single_lazy* Single_lazy::instance = NULL;
bool Single_lazy::lock = false;
Single_lazy* Single_lazy::getInstance(){
if(instance == NULL){
/* 沒有生成示例,首先上鎖 */
if(!lock){
lock = true;
/* 第二次檢測是否已經生成示例 */
if(instance == NULL){
instance = new Single_lazy();
}else;
lock = false;
}else;
}else;
return instance;
}
int main(){
Single_lazy* single_1 = Single_lazy::getInstance();
Single_lazy* single_2 = Single_lazy::getInstance();
if(single_1 == single_2)
std::cout << "single_1與single_2的地址一致" << std::endl << std::endl;
#ifdef debug
std::cout << "single_1:" << single_1->test << std::endl;
std::cout << "single_2:" << single_1->test << std::endl << std::endl;
single_1->test = 5;
std::cout << "single_1:" << single_1->test << std::endl;
std::cout << "single_2:" << single_1->test << std::endl << std::endl;
single_2->test = 10;
std::cout << "single_1:" << single_1->test << std::endl;
std::cout << "single_2:" << single_1->test << std::endl << std::endl;
#endif
std::cin.get();
return 0;
}
懶漢模式在原有的普通單例模式的基礎上增加了鎖,保證有且僅有一個線程能夠創建實例,避免了多線程導致創建多個實例出來。