C++單列模式_學到牛牛
單例模式是一種最為常見的軟件設計模式。單例模式要求:單例對象所在的類必須保證只能創(chuàng)建一個對象。單例模式在我們日常生活和軟件開發(fā)中的應用比比皆是,比如:windows系統只有一個任務管理器,一個市只有一個市長。
如何保證一個類最多只能創(chuàng)建一個對象呢?這個問題不能交由使用者去做處理,比如用全局變量。而應該由這個類的創(chuàng)建者在實現該類的時候考慮問題的解決。
單例模式巧妙的使用C++成員權限,將構造函數和拷貝構造函數隱藏起來(private),從而有效限定使用中對對象的自由創(chuàng)建。然后開放一個(static)接口,通過靜態(tài)方法創(chuàng)建對象,并在靜態(tài)方法中限定對象的唯一創(chuàng)建。

單例模式的創(chuàng)建方式一般有兩種:懶漢式和餓漢式。
1.懶漢模式
懶漢:顧名思義,不到萬不得已該類不會去實例化對象。將對象的示例推遲到需要該對象的時候。
// 單例模式之懶漢模式
class Singleton{
public:
static Singleton* createSingleton(){ // static方法
if( m_handler == nullptr ){
m_handler = new Singleton();
}
return m_handler;
}
private:
Singleton(); //? 私有化構造函數
~Singleton(); //? 私有化析構函數
Singleton( const Singleton & ); //? 私有化拷貝構造函數,防止通過拷貝構造復制對象
static Singleton* m_handler;
};
Singleton* Singleton::m_handler = nullptr;
int main()
{
Singleton *ptr1 = Singleton::createSingleton();
Singleton *ptr2 = ptr1-> createSingleton();? // ptr1 和 ptr2 指向同一個對象
return 0;
}
2.餓漢模式
餓漢:單例類在創(chuàng)建類的時候就創(chuàng)建了對象。
// 單例模式之餓漢模式
class Singleton{
public:
static Singleton* getSingleton(){
return m_handler;
}
private:
Singleton(); //? 私有化構造函數
~Singleton(); //? 私有化析構函數
Singleton( const Singleton & ); //? 私有化拷貝構造函數,防止通過拷貝構造復制對象
static Singleton* m_handler;
};
Singleton* Singleton::m_handler = new Singleton; // 類創(chuàng)建時,創(chuàng)建對象
int main()
{
Singleton *ptr1 = Singleton::createSingleton();
Singleton *ptr2 = ptr1-> createSingleton();? // ptr1 和 ptr2 指向同一個對象
return 0;
}
3.單例模式中的線程安全
前面我們考慮了單例模式的懶漢式和餓漢式,但是我們只考慮了普通單線程情況。如果考慮到多線程情況,那么上面的懶漢模式則不是線程安全的。而餓漢模式因為在編譯階段已經創(chuàng)建了對象,所有它是線程安全的。
如何解決懶漢模式的線程不安全呢?通常情況我們可以通過互斥鎖解決臨界資源的訪問問題。
// 單例模式之懶漢模式+線程安全
class Singleton{
public:
static Singleton* createSingleton(){ // static方法
if( m_handler == nullptr ){? ?// 解決訪問效率問題
pthread_mutex_lock( &m_lock );
if( m_handler == nullptr ){
m_handler = new Singleton();
}
pthread_mutex_unlock( &m_lock );
return m_handler;
}
}
private:
Singleton(); //? 私有化構造函數
~Singleton(); //? 私有化析構函數
Singleton( const Singleton & ); //? 私有化拷貝構造函數,防止通過拷貝構造復制對象
static Singleton* m_handler;
static pthread_mutex_t? m_lock;
};
Singleton* Singleton::m_handler = nullptr;
pthread_mutex_t? Singleton::m_lock = PTHREAD_MUTEX_INITIALIZER;
int main()
{
Singleton *ptr1 = Singleton::createSingleton();
Singleton *ptr2 = ptr1-> createSingleton();? // ptr1 和 ptr2 指向同一個對象
return 0;
}
上面例程通過互斥鎖,看似解決了多線程中的臨界資源互斥問題。但是實際上并非如此。具體問題如下:
上面代碼中:m_handler = new Singleton();? 我們期望的執(zhí)行順序是:
(1)分配一段內存? ?(2)構造對象,放入內存? ?(3)m_handler存內存地址
但是實際執(zhí)行可能是:
(1)分配一段內存? ?(2) m_handler存內存地址? ?(3)構造對象,放入內存
那么后面的情況可能導致,對象還沒創(chuàng)建,但是已經被另外一個線程拿去使用了,這種情況可能導致嚴重錯誤。那么如何解決呢?大家可以思考一下。
文章來源:學到牛牛 www.xuedaon.com