單例模式-解構函式的深入理解
阿新 • • 發佈:2018-12-23
singleton單例模式 單件模式
保證一個類中僅有一個例項,並且提供一個訪問他的全域性訪問點
a. 懶漢式:使用的時候才建立,多執行緒訪問的時候執行緒不安全(雙檢鎖)
b. 餓漢式:類檔案載入的時候已經建立好了物件,如果物件一直沒有使用,則類物件浪費空間
#注意
1. 建構函式私有化,
2. 私有的靜態類指標指向類的例項(類外宣告)
3. 共有的靜態方法去獲取一個訪問例項的訪問點
4. 解構函式需要定義一個巢狀類的解構函式去釋放單例
5. 絕對不要在單例的解構函式中釋放單例(coredump)!!!!!!!!! 詳情看下面有問題的單例程式碼
所以 單例解構函式:定義一個靜態的巢狀類物件的解構函式去析構單例物件 ,簡單思想看如下程式碼:
需要清楚一下幾點: 1、單例中的 new 的物件需要delete釋放。 2、delete釋放物件的時候才會呼叫物件的解構函式。 3、如果在解構函式裡呼叫delete,那麼程式結束時,根本進不去解構函式,怎麼會delete。 4、如果程式結束能自動析構,那麼就會造成一個析構的循壞,所以new對應於delete。 ///////////////////////////////////////////////////////////////////////////////////////////////// A.懶漢式單例模式
a. 懶漢式:使用的時候才建立,多執行緒訪問的時候執行緒不安全(雙檢鎖)
b. 餓漢式:類檔案載入的時候已經建立好了物件,如果物件一直沒有使用,則類物件浪費空間
#注意
1. 建構函式私有化,
2. 私有的靜態類指標指向類的例項(類外宣告)
3. 共有的靜態方法去獲取一個訪問例項的訪問點
4. 解構函式需要定義一個巢狀類的解構函式去釋放單例
5. 絕對不要在單例的解構函式中釋放單例(coredump)!!!!!!!!! 詳情看下面有問題的單例程式碼
#include <iostream> using namespace std; class A { private: static A *m_p; public: static A *getSingleTon() { if(NULL == m_p) { m_p = new A(); } return m_p; } /* ~A() { if(NULL != m_p) { cout << "xxx" << endl; delete m_p; // 遞迴呼叫析構 m_p = NULL; cout << "yyy" << endl; // 永遠也不會執行 } } */ }; A* A::m_p = NULL; int main() { A *p = A::getSingleTon(); delete p; return 0; }
所以 單例解構函式:定義一個靜態的巢狀類物件的解構函式去析構單例物件 ,簡單思想看如下程式碼:
#include<iostream> using namespace std; //巢狀類去析構單例類物件 class A { public: ~A() { cout << "deconstructor A" << endl; } class B { public: ~B() //巢狀類成員的解構函式去析構單例函式的物件 { cout << "呼叫巢狀 deconstructor B" << endl; } }; private: static B b; static int a; }; int A::a = 10; A::B A::b; //巢狀靜態類成語,必須類外宣告 int main() { return 0; }
需要清楚一下幾點: 1、單例中的 new 的物件需要delete釋放。 2、delete釋放物件的時候才會呼叫物件的解構函式。 3、如果在解構函式裡呼叫delete,那麼程式結束時,根本進不去解構函式,怎麼會delete。 4、如果程式結束能自動析構,那麼就會造成一個析構的循壞,所以new對應於delete。 ///////////////////////////////////////////////////////////////////////////////////////////////// A.懶漢式單例模式
//懶漢式 (建立的時候採取new例項)以時間換取空間,執行緒不安全
class singleton_L
{
public:
static singleton_L* getinstance()
{
if(instance == NULL)
{
instance = new singleton_L();
}
return instance;
}
//實際上在解構函式中析構物件是一種錯誤的做法,會呼叫,切是一個死遞迴,正確做法是建立一個巢狀的析構類
/*~singleton_L()
{
if(instance != NULL)
{
delete instance;
instance = NULL;
}
cout << "~singleton_L()" << endl;
}*/
private:
class Garbo //設定為私有防止外界訪問
{
public:
~Garbo()//實際去析構new的單例物件
{
if(singleton_L::instance != NULL)
{
cout << "delete singleton" << endl;
delete singleton_L::instance;
singleton_L::instance = NULL;
}
}
};
private:
singleton_L(){}
static singleton_L* instance; //單例唯一實力
static Garbo garbo; //靜態私有的巢狀類物件,防止被外界訪問
};
singleton_L* singleton_L::instance = NULL;
singleton_L::Garbo singleton_L::garbo; //靜態類物件類外聲
B懶漢式 ,執行緒安全方法
//懶漢式 執行緒安全式(雙檢鎖)
/*
所謂雙重檢查加鎖機制,指的是:並不是每次進入getInstance方法都需要同步,而是先不
同步,進入方法過後,先檢查例項是否存在,如果不存在才進入下面的同步塊,這是第一重
檢查。進入同步塊過後,再次檢查例項是否存在,如果不存在,就在同步的情況下建立一個
例項,這是第二重檢查。這樣一來,就只需要同步一次了,從而減少了多次在同步情況下進
行判斷所浪費的時間。
*/
class singleton_L_Lock
{
public:
static singleton_L_Lock* getinstance()
{
if(instance == NULL)
{
pthread_mutex_lock(&mutex); //加鎖
if(instance == NULL)
instance = new singleton_L_Lock();
pthread_mutex_unlock(&mutex);
}
return instance;
}
private:
class GGGarbo
{
public:
~GGGarbo()
{
if(singleton_L_Lock::instance != NULL)
{
delete singleton_L_Lock::instance;
singleton_L_Lock::instance = NULL;
}
}
};
public:
static pthread_mutex_t mutex;
private:
singleton_L_Lock()
{
pthread_mutex_init(&mutex, NULL);
}
private:
static singleton_L_Lock* instance;
static GGGarbo gggarbo;
};
singleton_L_Lock* singleton_L_Lock::instance = NULL;
pthread_mutex_t singleton_L_Lock::mutex;
singleton_L_Lock::GGGarbo singleton_L_Lock::gggarbo;
c.餓漢式
,不牽扯執行緒安不安全,以空間換時間
//餓漢式(在定義例項的時候就去new物件)以空間換時間
class singleton_E
{
public:
static singleton_E* getinstance()
{
return instance;
}
// ~singleton_E(){}
class GGarbo
{
public:
~GGarbo()
{
if(singleton_E::instance != NULL)
{
cout << "delete singleton_E" << endl;
delete singleton_E::instance;
singleton_E::instance = NULL;
}
}
};
private:
singleton_E(){}
static singleton_E* instance;
static GGarbo ggarbo;
};
singleton_E* singleton_E::instance = new singleton_E();
singleton_E::GGarbo singleton_E::ggarbo;
以上就是我對單例模式的一些理解心得,如有不對的地方多多指教,哈哈哈。