title: 設(shè)計模式之單例模式
longzy:2019-2-23
單列模式颠印,顧名思義,一個類只有一個實(shí)例拥刻。所以單列模式的特征為:
- 只有一個實(shí)例
- 必須提供一個全局訪問點(diǎn)(靜態(tài)成員方法或靜態(tài)靜態(tài)成員變量)
- 不可以復(fù)制拷貝
- 如果得到是靜態(tài)成員對象指針的地址怜瞒,必須提供一個釋放指針的方法
根據(jù)以上描述的特征,那么一個簡單的單例模式就誕生了般哼,如下代碼所示
template <typename T>
class Singleton
{
public:
static T* Instance()
{
if (m_pInstance == nullptr)
m_pInstance = new T();
return m_pInstance;
}
static void DestroyInstance()
{
if (m_pInstance != nullptr)
{
delete m_pInstance;
m_pInstance = nullptr;
}
}
private:
Singleton(){}
~Singlento(){}
Singleton(const Singleton&);
Singleto& operator = (const Singleton&);
private:
static T* m_pInstance;
};
template <typename T>
T* Singleton<T>::m_pInstance = nullptr;
我們分析下上面代碼盼砍,如果在單線程環(huán)境下面運(yùn)行尘吗,沒有什么問題,假設(shè)在多線程環(huán)境中浇坐,兩個線程同時運(yùn)行到m_pInstance == nullptr,此時條件為真睬捶,那么就會創(chuàng)建兩個實(shí)例,這不符合單列的特征近刘,那么在這里需要改進(jìn)擒贸,在改進(jìn)前,我們先用c++11實(shí)現(xiàn)一個不可復(fù)制的類Noncopyable觉渴,以后讓其繼承該類即可
class Noncopyable
{
protected:
Noncopyable() = default;
~Noncopyable() = default;
Noncopyable(const Noncopyable&) = delete;
Noncopyable& operator = (const Noncopyable&) = delete;
};
然后改進(jìn)后的單列類如下
#include <mutex>
template <typename T>
class Singleton : Noncopyable
{
public:
static T* Instance()
{
std::lock_guard<std::mutex> lock(m_mutex);
if (m_pInstance == nullptr)
{
m_pInstance = new T();
}
return m_pInstance;
}
static void DestroyInstance()
{
std::lock_guard<std::mutex> lock(m_mutex);
if (m_pInstance != nullptr)
{
delete m_pInstance;
m_pInstance = nullptr;
}
}
private:
static T* m_pInstance;
static std::mutex m_mutex;
};
template <typename T>
T* Singleton<T>::m_pInstance = nullptr;
template <typename T>
std::mutex Singleton<T>::m_mutex;
我們分析下上面代碼介劫,解決線程安全問題無非就是加鎖,但是如果線程很多情況下案淋,每個線程都要等拿到鎖的線程運(yùn)行結(jié)束后才繼續(xù)執(zhí)行座韵,這樣無疑會導(dǎo)致大量的線程阻塞,那么該如何解決呢踢京,解決辦法就是在鎖之前先判斷是否為nullptr誉碴,于是改進(jìn)后的代碼如下:
#include <mutex>
template <typename T>
class Singleton : Noncopyable
{
public:
static T* Instance()
{
if (m_pInstance == nullptr)
{
std::lock_guard<std::mutex> lock(m_mutex);
if (m_pInstance == nullptr)
{
m_pInstance = new T();
}
}
return m_pInstance;
}
static void DestroyInstance()
{
if (m_pInstance != nullptr)
{
std::lock_guard<std::mutex> lock(m_mutex);
if (m_pInstance != nullptr)
{
delete m_pInstance;
m_pInstance = nullptr;
}
}
}
private:
static T* m_pInstance;
static std::mutex m_mutex;
};
template <typename T>
T* Singleton<T>::m_pInstance = nullptr;
template <typename T>
std::mutex Singleton<T>::m_mutex;
繼續(xù)分析上面的代碼,其實(shí)這就是所謂的雙檢鎖機(jī)制瓣距。但是請注意黔帕,如果數(shù)據(jù)量很大的情況,加鎖釋放鎖本來就是耗時的操作蹈丸,所以在大數(shù)據(jù)情況下成黄,這種雙檢鎖機(jī)制的單列模式性能就顯得堪憂了,所以我們應(yīng)該避免加鎖操作逻杖,于是就出現(xiàn)了另外一種單列模式
template <typename T>
class Singleton : Noncpyable
{
public:
static T* Instance()
{
return m_pInstance;
}
private:
static T* m_pInstance;
};
template <typename T>
T* Singleton<T>::m_pInstance = new T();
繼續(xù)分析上面代碼奋岁,巧妙的使用了靜態(tài)成員初始化的特性,靜態(tài)成員初始化是在程序進(jìn)入主函數(shù)之前荸百,主線程以單線程的方式完成了初始化操作闻伶,所以很好的解決了線程安全問題。但是沒有提供一個銷毀靜態(tài)實(shí)例的方法管搪。于是我們可以考慮返回靜態(tài)成員對象的地址,然后利用對象的自動銷毀功能來做釋放操作铡买,于改進(jìn)后的代碼如下:
template <typename T>
class Singleton : Noncopyable
{
public:
static T* Instance()
{
return &m_Instance;
}
private:
static T m_Instance;
};
template <typename T>
T Singleton<T>::m_Instance;
這樣的實(shí)現(xiàn)應(yīng)該算是比較好了更鲁,但是還有比這更好的完美方法,利用linux的pthread_once和c++11的std::call_once
template <typename T>
class Singleton : Noncopyable
{
public:
static T* Instance()
{
std::call_once(m_flag,&Singleton::Init); //c++11
//pthread_once(&m_ponce,&Singleton::Init); //linux
return m_pInstance;
}
static void DestroyInstance()
{
delete m_pInstance;
m_pInstance = nullptr;
}
private:
static T* m_pInstance;
static std::once_flag m_flag;
//static pthread_once_t m_ponce;
static void Init()
{
m_pInstance = new T();
}
};
//template <typename T>
//pthread_once_t Singleton<T>::m_ponce = PTHREAD_ONCE_INIT;
template <typename T>
T* Singleton<T>::m_pInstance = nullptr;
另外放一個超級大招奇钞,利用c++11的可變模板參數(shù)澡为,放一個萬能的單列模式
template <typename T>
class Singleton : Noncopyable
{
public:
template <typename... Args>
static T* Instance(Args... args)
{
std::call_once(m_flag,&Singleton::Init,args);
return m_pInstance;
}
static void DestroyInstance()
{
delete m_pInstance;
m_pInstance = nullptr;
}
private:
static T* m_pInstance;
static std::once_flag = m_flag;
template <typename... Args>
static void Init(Args&&.. args)
{
return new T(std::forward<Args>(args)...);
}
};
template <typename T>
T* Singleton<T>::m_pInstance = nullptr;
template <typename T>
std::once_flag Singleton<T>::m_flag;
好了,單列模式就分析到這里了景埃,最后一種是萬金油媒至。
上一個測試代碼吧
#include <iostream>
#include "Singleton4.hpp"
struct A
{
A()
{
std::cout << "A is constrcut!" << std::endl;
num = 0;
}
~A()
{
std::cout << "A is desconstruct!" << std::endl;
}
int num;
void add(int n)
{
num = num + n;
}
int getNum()
{
return num;
}
};
struct B
{
int a_;
explicit B(int a) : a_(a)
{
}
int get()
{
return a_;
}
};
int main()
{
A *a = Singleton<A>::Instance();
A *b = Singleton<A>::Instance();
std::cout << "a is" << a << std::endl;
std::cout << "b is" << b << std::endl;
a->add(5);
std::cout << "b.num=" << b->getNum() << std::endl;
b->add(10);
std::cout << "a.num=" << a->getNum() << std::endl;
std::cout << "b.num=" << b->getNum() << std::endl;
B *bb = Singleton<B>::Instance(5);
std::cout << bb << std::endl;
std::cout << "bb.a=" << bb->get() << std::endl;
}
由于本人水平有限顶别,若有錯誤,歡迎指出拒啰,謝謝驯绎!