在上篇博文中,我們分析了適配器模式,適配器模式通俗地來講就是實現了接口之間的轉換,是之前不能夠使用的接口能夠在新的環境下使用,今天我們要學習的是另外一個設計模式——單例模式,說到單例模式想必大家都應該明白,通俗地說就是隻能產生一個對象的類,個人認爲這種設計模式在整個設計模式中都是最簡單的,並且這種模式最大的好處就是對象易於管理,這種模式在我的工作中也是時常看到,下面我們就來看看這個單例模式,代碼如下:
#ifndef __SINGLETON__H
#define __SINGLETON__H
#include <iostream>
#include <string>
#include <boost/smart_ptr.hpp>
using namespace std;
using namespace boost;
class Book
{
public:
Book(string bookName = string(),const int price = 0):bookName(bookName),price(price)
{
}
~Book(){}
void setBookName(string bookName)
{
this->bookName = bookName;
}
void setPrice(const int price)
{
this->price = price;
}
void display()
{
cout<<"book:"<<bookName<<" "<<price<<endl;
}
private:
string bookName;
int price;
};
template<class T>
class Singleton
{
public:
static shared_ptr<T> getSingleton()
{
if(object.use_count() == 0)
object = shared_ptr<T>(new T());
return object;
}
private:
Singleton(){}
private:
static shared_ptr<T> object;
};
template<class T>
shared_ptr<T> Singleton<T>::object;
#endif
測試代碼:
#include "Singleton.h"
int main()
{
shared_ptr<Book> book =Singleton<Book>::getSingleton();
book->setBookName("C++");
book->setPrice(20);
shared_ptr<Book> book1 = Singleton<Book>::getSingleton();
book1->display();
return 0;
}
測試結果:
book:C++ 20
依據單例模式的定義:只有一個對象的類,上述代碼中實現了這種方式,即將構造函數設置爲私有的,並且只提供一個訪問對象的接口,在上述代碼中通過採用shared_ptr智能指針可以防止內存泄露問題,這種寫法相比傳統的方式應該要安全點,但是其所凸顯的問題依然存在,即在多線程的環境中,同樣有不安全的問題,下面我們就來改進下上述的實現方法,使其在多線程環境下也能夠正常工作,代碼如下:
#ifndef __SINGLETON__H
#define __SINGLETON__H
#include <iostream>
#include <string>
#include <boost/smart_ptr.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
using namespace std;
using namespace boost;
boost::mutex mut;
class Book
{
public:
Book(string bookName = string(),const int price = 0):bookName(bookName),price(price)
{
}
~Book(){}
void setBookName(string bookName)
{
mut.lock();
this->bookName = bookName;
mut.unlock();
}
void setPrice(const int price)
{
mut.lock();
this->price = price;
mut.unlock();
}
void display()
{
mut.lock();
cout<<"book:"<<bookName<<" "<<"price:"<<price<<endl;
mut.unlock();
}
private:
string bookName;
int price;
};
template<class T>
class Singleton
{
public:
static shared_ptr<T> getSingleton()
{
if(object.use_count() == 0)
{
mut.lock();
if(object.use_count()==0)
object = shared_ptr<T>(new T());
mut.unlock();
}
return object;
}
static int use_count()
{
return object.use_count();
}
private:
Singleton(){}
private:
static shared_ptr<T> object;
};
template<class T>
shared_ptr<T> Singleton<T>::object;
class task
{
public:
task(string bookName = string(),const int price = 0):bookName(bookName),price(price){}
~task(){}
task(const task& tsk)
{
bookName = tsk.bookName;
price = tsk.price;
}
task& operator = (const task& tsk)
{
bookName = tsk.bookName;
price = tsk.price;
}
void operator()()const
{
Singleton<Book>::getSingleton()->setBookName(bookName);
Singleton<Book>::getSingleton()->setPrice(price);
Singleton<Book>::getSingleton()->display();
cout<<Singleton<Book>::getSingleton().use_count()<<endl;
}
private:
string bookName;
int price;
};
#endif
include "Singleton.h"
int main()
{
boost::thread thr1(task("C++",20));
boost::thread thr2(task("Java",40));
boost::thread thr3(task("Python",60));
thr1.join();
thr2.join();
thr3.join();
return 0;
}
測試結果:
book:Python price:60
4
book:Java price:40
3
book:C++ price:20
2
上述代碼已經對對象進行了加鎖處理,這種方式也被稱爲“DOUBLE CHECK”,就是在創建對象時,對象被檢查了兩次,這兩次都是有必要的,可能會有人說直接將mut放到函數開頭不就行了,也沒必要搞DOUBLE CHECK,在線程不多時,這種方式可能會有作用,但是當線程數量巨大時,這種方式會嚴重影響到系統系能,這個可以通過實驗來分析,主要是因爲線程每次訪問對象時,都需要獲取鎖,而採用double check的方式,則不會,好好地體會下
總結
本篇博文主要分析了下單例模式,這個模式在實際應用中很廣泛,在我的工作中經常看到,這個模式也是最簡單的,但是如何靈活地運用,這個只有在實際開發中,好好地體會了,在單例模式中,我們會很在意在多線程環境下的單例模式安全性問題,確實,在實際的開發中,由於只有一個對象,因此如何在線程間安全地訪問這個對象,將會成爲開發中涉及到單例時不可不考慮的問題,其實如果簡單的程序應用,完全可以採用加大鎖的方式,如果涉及到一些對實時性要求較高的場所,可以嘗試一些比較精確的分塊加鎖的機制,在這裏就不討論了,好了,本篇博文到此結束,下篇我們繼續分析設計模式之——原型模式。
如果需要,請註明轉載,多謝