在上篇博文中,我们分析了适配器模式,适配器模式通俗地来讲就是实现了接口之间的转换,是之前不能够使用的接口能够在新的环境下使用,今天我们要学习的是另外一个设计模式——单例模式,说到单例模式想必大家都应该明白,通俗地说就是只能产生一个对象的类,个人认为这种设计模式在整个设计模式中都是最简单的,并且这种模式最大的好处就是对象易于管理,这种模式在我的工作中也是时常看到,下面我们就来看看这个单例模式,代码如下:
#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的方式,则不会,好好地体会下
总结
本篇博文主要分析了下单例模式,这个模式在实际应用中很广泛,在我的工作中经常看到,这个模式也是最简单的,但是如何灵活地运用,这个只有在实际开发中,好好地体会了,在单例模式中,我们会很在意在多线程环境下的单例模式安全性问题,确实,在实际的开发中,由于只有一个对象,因此如何在线程间安全地访问这个对象,将会成为开发中涉及到单例时不可不考虑的问题,其实如果简单的程序应用,完全可以采用加大锁的方式,如果涉及到一些对实时性要求较高的场所,可以尝试一些比较精确的分块加锁的机制,在这里就不讨论了,好了,本篇博文到此结束,下篇我们继续分析设计模式之——原型模式。
如果需要,请注明转载,多谢