單例模式:
單例是設(shè)計模式的一種,保證一個類只有一個實例,并提供一個可以訪問它的全局訪問點。
通??梢宰屢粋€全局變量使得一個對象被訪問,但是不能阻止實例化多個對象,解決辦法是:讓類自身負責保存它的唯一實例,這個類保證沒有其他的實例可以被創(chuàng)建,并且它提供一個可以訪問該實例的方法。
Singleton類中,定義一個getInstance()靜態(tài)成員函數(shù),允許用戶訪問它的唯一的實例,getInstance是一個靜態(tài)方法,主要負責創(chuàng)建自己的唯一實例。
//不安全的單例模式 存在線程安全和內(nèi)存泄漏
class Singleton1 {
private:
Singleton1() { cout<< "Singleton1 Construct!"<<endl;}
Singleton1(const Singleton1 &) = delete;
Singleton1 & operator =(const Singleton1&) = delete;
static Singleton1* resptr_1;
public:
~Singleton1() {cout<< "Singleton1 Destruct"<<endl;}
static Singleton1* getInstance();
void func();
};
Singleton1* Singleton1::getInstance() {
if (resptr_1 == nullptr) {
resptr_1 = new Singleton1;
}
return resptr_1;
}
void Singleton1::func () {
cout << "do something here!"<< endl;
}
客戶端代碼:
Singleton1* Singleton1::resptr_1 = nullptr;
int main() {
Singleton1 * instance1 = Singleton1::getInstance();
Singleton1 * instance2 = Singleton1::getInstance();
instance1->func();
instance2->func();
if (instance1== instance2)
cout << "instance1 == instance2"<<endl;
cout << endl;
system("pause");
return 0;
}

執(zhí)行結(jié)果
上述代碼有內(nèi)存泄漏的問題,以及再多線程情況下存在線程安全的情況,對于以上情況,可以使用雙檢鎖來解決線程安全的問題,使用智能指針托管創(chuàng)建的實例。如下類Singletion2實現(xiàn)
//shared_ptr 解決內(nèi)存泄漏,雙檢鎖解決線程安全
class Singleton2 {
private:
Singleton2() { cout << "Singleton2 Construct!"<< endl;}
static shared_ptr<Singleton2> resptr_2;
static mutex mutex_;
//void func();
public:
~Singleton2() {cout<< "Singleton2 Destruct"<<endl;}
Singleton2(const Singleton2&) = delete;
Singleton2& operator =(const Singleton2&) = delete;
static shared_ptr<Singleton2> getInstance();
void func();
};
shared_ptr<Singleton2> Singleton2::getInstance() {
if (resptr_2 == nullptr) {
lock_guard<mutex> lk(mutex_);
if (resptr_2 == nullptr) {
resptr_2 = shared_ptr<Singleton2>(new Singleton2);
}
}
return resptr_2;
}
void Singleton2::func() {
cout << "do something here!"<< endl;
}
客戶端代碼:
shared_ptr<Singleton2> Singleton2::resptr_2 = nullptr;
mutex Singleton2::mutex_;
int main() {
shared_ptr<Singleton2> instance3 = Singleton2::getInstance();
shared_ptr<Singleton2> instance4 = Singleton2::getInstance();
instance3->func();
instance4->func();
if (instance3 == instance4)
cout << "instance3 == instance4"<<endl;
cout <<endl;
system("pause");
return 0;
}

image.png
C++11標準中的Magic Static特性:如果當變量在初始化的時候,并發(fā)同時進入聲明語句,并發(fā)線程將會阻塞等待初始化結(jié)束。利用該特性就可以寫出一根非常好的線程安全的單例模式代碼。如下Singleton3:
/局部靜態(tài)變量的方式。如果當變量在初始化的時候,并發(fā)同時進入聲明語句,并發(fā)線程將會阻塞等待初始化結(jié)束
class Singleton3 {
private:
Singleton3() {cout << "Singleton3 Construct!"<<endl;}
public:
~Singleton3() {cout<< "Singleton3 Destruct"<<endl;}
Singleton3(const Singleton3&) = delete;
Singleton3& operator =(const Singleton3&) = delete;
static Singleton3& getInstance();
void func();
};
Singleton3& Singleton3::getInstance() {
static Singleton3 instance;
return instance;
}
void Singleton3::func() {
cout << "do something here!"<<endl;
}
客戶端代碼:
int main() {
Singleton3 & instance5 = Singleton3::getInstance();
Singleton3 & instance6 = Singleton3::getInstance();
instance5.func();
instance6.func();
if (&instance5 == &instance6)
cout << "instance5 == instance6"<<endl;
system("pause");
return 0;
}

image.png
人們又根據(jù)實例的創(chuàng)建時間將單例模式分為了懶漢式和餓漢式
餓漢式:類構(gòu)造的時候就創(chuàng)建實例對象,空間換時間的策略
懶漢式:需要的時候,創(chuàng)建實例,時間換空間的策略
很明顯上述的三款代碼都是懶漢式的單例模式,餓漢式的特點是線程安全的,因為在類產(chǎn)生的時候就已經(jīng)將實例創(chuàng)建了每次獲取的收直接返回的是預先創(chuàng)建好額實例,也就不存在創(chuàng)建多個的情況了,餓漢式的單例模式如下所示:
class Singleton {
private:
Singleton() { cout<< "Singleton Construct!"<<endl;}
Singleton(const Singleton &) = delete;
Singleton & operator =(const Singleton&) = delete;
static Singleton instance;//創(chuàng)建實例。
public:
~Singleton() {cout<< "Singleton Destruct"<<endl;}
static Singleton* getInstance();
void func();
};
Singleton* Singleton::getInstance() {
return &instance;
}
void Singleton::func () {
cout << "do something here!"<< endl;
}