設計模式——單例模式C++實現
引出問題:
設計一個類,我們只能生成該類的一個物件例項。
不好的解法:只適用於單執行緒環境
因為該類只能生成一個物件例項,那麼該類的建構函式必須是私有的,從而避免他人建立例項。在需要的時候建立該類的一個物件。
下面是程式實現:
/*************************************************************************
> File Name: SigleInstance.cpp
> Author:
> Mail:
> Created Time: Sat 18 Jun 2016 06:44:44 PM CST
************************************************************************/
#include <iostream>
using namespace std;
//只能生成一個類物件的類
class SigleInstance
{
public:
//指向類的一個例項物件
static SigleInstance * instance;
private:
//私有的建構函式
SigleInstance(){cout << "執行SigleInstance 建構函式" << endl;}
public:
//建立一個例項物件
static SigleInstance * getSigleInstance(){
if(instance == NULL)
instance = new SigleInstance();
return instance;
}
};
//初始化靜態成員變數
SigleInstance * SigleInstance::instance = NULL;
//測試函式
int main()
{
SigleInstance * SigleInstance1 = SigleInstance::getSigleInstance();
SigleInstance * SigleInstance2 = SigleInstance::getSigleInstance();
SigleInstance * SigleInstance3 = SigleInstance::getSigleInstance();
if(SigleInstance1 == SigleInstance2){
cout << "SigleInstance1 == SigleInstance2" << endl;
}
if(SigleInstance1 == SigleInstance3){
cout << "SigleInstance1 == SigleInstance3" << endl;
}
return 0;
}
下面是程式的輸出: 執行SigleInstance 建構函式 SigleInstance1 == SigleInstance2 SigleInstance1 == SigleInstance3
從程式的輸出可以看出,SigleInstance1和SigleInstance2和SigleInstance3指向的都是同一個物件。
不好的解法2:可以在多執行緒環境下工作,但是效率比較低
解法1中的程式碼在單執行緒的情況下工作正常,但在多執行緒的環境下就有問題了。如果兩個執行緒同時執行判斷instance是否為NULL的if語句,並且instance例項還沒有建立時,那麼兩個執行緒都會建立一個例項,那麼此時就不滿足單例模式的要求了。
為了保證在多執行緒的環境下我們還是隻能得到一個例項,需要加上一個同步鎖。
下面是實現的程式:
/*************************************************************************
> File Name: SigleInstance.cpp
> Author:
> Mail:
> Created Time: Sat 18 Jun 2016 06:44:44 PM CST
************************************************************************/
#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;
//只能生成一個類物件的類
class SigleInstance
{
public:
//指向類的一個例項物件
static SigleInstance * instance;
//定義一個互斥鎖變數
static pthread_mutex_t instanceMutex;
private:
//私有的建構函式
SigleInstance(){cout << "執行SigleInstance 建構函式" << endl;}
public:
//建立一個例項物件
static SigleInstance * getSigleInstance(){
//獲得互斥鎖
pthread_mutex_lock(&instanceMutex);
if(instance == NULL)
instance = new SigleInstance();
//睡眠僅僅為了測試其它執行緒是否處於等待狀態,真正的程式是不需要該延遲的
sleep(3);
//釋放互斥鎖
pthread_mutex_unlock(&instanceMutex);
return instance;
}
};
//初始化靜態成員變數
SigleInstance * SigleInstance::instance = NULL;
//初始化互斥鎖變數
pthread_mutex_t SigleInstance::instanceMutex = PTHREAD_MUTEX_INITIALIZER;
//執行緒要執行的函式
void * threadFun(void *arg)
{
SigleInstance * instance = SigleInstance::getSigleInstance();
cout << "theread" << dec << pthread_self() << ", instance's address = " << hex << instance << endl;
pthread_exit(NULL);
}
//測試函式
int main()
{
const int NUM = 3;
pthread_t threadId[NUM];
//建立NUM個執行緒
for(int i = 0; i < NUM; ++i){
pthread_create(&threadId[i], NULL, threadFun, NULL);
}
for(int i = 0; i < NUM; ++i){
pthread_join(threadId[i], NULL);
}
return 0;
}
下面是程式的輸出:
執行SigleInstance 建構函式
theread139706800436992, instance's address = 0x7f10000008c0
theread139706808829696, instance's address = 0x7f10000008c0
theread139706817222400, instance's address = 0x7f10000008c0
從輸出結果可以看出,執行了一個建構函式,每個執行緒獲得的例項物件的地址都是相同的,表明只有一個例項物件。並且每個執行緒之間的等待時間為2秒。
在上面的程式中,因為每個時刻只能有一個執行緒獲得互斥鎖,當第一個執行緒獲得互斥鎖的時候,其它執行緒只能等待。當第一個執行緒獲得互斥鎖後,發現例項還沒有建立時,它會建立一個例項。接著第一個執行緒釋放互斥鎖,此時第二個執行緒可以獲得互斥鎖,並執行下面的程式碼,但是此時已經建立了一個例項,所以,第二個執行緒直接返回,不會再建立一個例項。這樣就可以保證在多執行緒的環境下也只能建立一個例項。
但是解法2不是很完美,因為每次通過獲取例項物件的時候都會試圖獲得互斥鎖,但是加鎖和解鎖是非常耗時的操作,我們應該儘量避免加鎖和解鎖的操作。具體改進看下面解法3。
改進解法2:加鎖前後兩次判斷例項是否已經存在
/*************************************************************************
> File Name: SigleInstance.cpp
> Author:
> Mail:
> Created Time: Sat 18 Jun 2016 06:44:44 PM CST
************************************************************************/
#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;
//只能生成一個類物件的類
class SigleInstance
{
public:
//指向類的一個例項物件
static SigleInstance * instance;
//定義一個互斥鎖變數
static pthread_mutex_t instanceMutex;
private:
//私有的建構函式
SigleInstance(){cout << "執行SigleInstance 建構函式" << endl;}
public:
//建立一個例項物件
static SigleInstance * getSigleInstance(){
//如果例項沒有建立,則加鎖並建立例項
//如果例項已經建立,則直接返回該例項的指標
if(instance == NULL){
//獲得互斥鎖
pthread_mutex_lock(&instanceMutex);
if(instance == NULL)
instance = new SigleInstance();
//睡眠僅僅為了測試其它執行緒是否處於等待狀態,真正的程式是不需要該延遲的
sleep(3);
//釋放互斥鎖
pthread_mutex_unlock(&instanceMutex);
}
return instance;
}
};
//初始化靜態成員變數
SigleInstance * SigleInstance::instance = NULL;
//初始化互斥鎖變數
pthread_mutex_t SigleInstance::instanceMutex = PTHREAD_MUTEX_INITIALIZER;
//執行緒要執行的函式
void * threadFun(void *arg)
{
SigleInstance * instance = SigleInstance::getSigleInstance();
cout << "theread" << dec << pthread_self() << ", instance's address = " << hex << instance << endl;
pthread_exit(NULL);
}
//測試函式
int main()
{
const int NUM = 3;
pthread_t threadId[NUM];
//建立NUM個執行緒
for(int i = 0; i < NUM; ++i){
pthread_create(&threadId[i], NULL, threadFun, NULL);
}
for(int i = 0; i < NUM; ++i){
pthread_join(threadId[i], NULL);
}
return 0;
}
下面是程式的輸出:
執行SigleInstance 建構函式
theread139991850669824, instance's address = 0x7f525c0008c0
theread139991859062528, instance's address = 0x7f525c0008c0
theread139991842277120, instance's address = 0x7f525c0008c0
從程式的輸出情況來看,執行緒139991842277120是建立該類例項的執行緒,因為,它經過了睡眠,所以是最後輸出。另外兩個執行緒輸出比較早,線上程139991842277120建立完例項之後,另外兩個執行緒就返回了,這兩個執行緒沒有進行加鎖和解鎖操作。
解法4:利用靜態變數的初始化順序
這種方法其實是單例模式中的餓漢式方式,上面的方法是單例模式中的懶漢式方式。
這種方法的例項物件,是在類變數instance初始化的時候就建立了該例項,也就是說程式一開始執行,
參考文章:
下面是程式實現:
/*************************************************************************
> File Name: SigleInstance.cpp
> Author:
> Mail:
> Created Time: Sat 18 Jun 2016 06:44:44 PM CST
************************************************************************/
#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;
//只能生成一個類物件的類
class SigleInstance
{
public:
//指向類的一個例項物件
static SigleInstance * instance;
private:
//私有的建構函式
SigleInstance(){cout << "執行SigleInstance 建構函式" << endl;}
public:
//建立一個例項物件
static SigleInstance * getSigleInstance(){
return instance;
}
};
//初始化靜態成員變數,此時直接建立一個例項物件
SigleInstance * SigleInstance::instance = new SigleInstance();
//執行緒要執行的函式
void * threadFun(void *arg)
{
SigleInstance * instance = SigleInstance::getSigleInstance();
cout << "theread" << dec << pthread_self() << ", instance's address = " << hex << instance << endl;
pthread_exit(NULL);
}
//測試函式
int main()
{
cout << "main start..." << endl;
const int NUM = 3;
pthread_t threadId[NUM];
//建立NUM個執行緒
for(int i = 0; i < NUM; ++i){
pthread_create(&threadId[i], NULL, threadFun, NULL);
}
for(int i = 0; i < NUM; ++i){
pthread_join(threadId[i], NULL);
}
return 0;
}
下面是程式的輸出:
執行SigleInstance 建構函式
main start...
theread140681870264064, instance's address = 0xbaa010
theread140681878656768, instance's address = 0xbaa010
theread140681887049472, instance's address = 0xbaa010
從輸出結果可以看出,在main函式開始執行之前,類例項已經建立完了。
更多詳情,請前往本人部落格網站:個人部落格網站
相關文章
- 【php實現設計模式】之單例模式PHP設計模式單例
- 用Python實現設計模式——單例模式Python設計模式單例
- 單例模式c++實現單例模式C++
- 設計模式回顧——單例模式(C++)設計模式單例C++
- Go 實現常用設計模式(一)單例模式Go設計模式單例
- 設計模式:單例模式的使用和實現(JAVA)設計模式單例Java
- 設計模式學習-使用go實現單例模式設計模式Go單例
- 設計模式(單例模式)設計模式單例
- [設計模式] 單例模式設計模式單例
- 設計模式-單例模式設計模式單例
- 設計模式 —— 單例模式設計模式單例
- 設計模式 單例模式設計模式單例
- 設計模式——單例模式設計模式單例
- 設計模式-單例模式、多例模式設計模式單例
- 設計模式之單例設計模式設計模式單例
- 設計模式(一)_單例模式設計模式單例
- 常用設計模式-單例模式設計模式單例
- 設計模式之單例模式設計模式單例
- Java設計模式【單例模式】Java設計模式單例
- Java設計模式 | 單例模式Java設計模式單例
- 001設計模式:單例模式設計模式單例
- # Python設計模式 單例模式Python設計模式單例
- 設計模式一(單例模式)設計模式單例
- 設計模式之☞單例模式設計模式單例
- Java設計模式——單例模式Java設計模式單例
- Java設計模式–單例模式Java設計模式單例
- js設計模式--單例模式JS設計模式單例
- Java設計模式-單例模式Java設計模式單例
- 設計模式(二)——單例模式設計模式單例
- 設計模式之---單例模式設計模式單例
- Java設計模式--單例模式Java設計模式單例
- Python設計模式——單例模式Python設計模式單例
- 設計模式—singleton(單例模式)設計模式單例
- python設計模式-單例模式Python設計模式單例
- Java設計模式——實現單例模式的七種方式[JZOF]Java設計模式單例
- 設計模式總結 —— 單例設計模式設計模式單例
- JavaScript設計模式初探--單例設計模式JavaScript設計模式單例
- 單例設計模式單例設計模式
- 設計模式學習(一)單例模式的幾種實現方式設計模式單例