C++设计模式之单例模式
一、使用情景
在很多环境下,我们需要保证某些特殊对象在整个系统中的唯一性。windows的资源管理器就是一个很好的例子。无论我们点击多少次“启动任务管理器”都只弹出来一个任务管理器窗口。因为如果弹出多个窗口,而这些窗口的内容都一模一样,全部都是重复的对象,那是没有意义的,而且还会浪费系统资源。而单例模式就能很好地在这种情景之下应用。
二、实现
单例模式在类外无法直接创建对象,只能通过类本身来创建对象。单例模式具有两个特点:
+ 该类只有一个对象
+ 类必须自行创建这个实例对象
单例模式的结构如下图所示:
三、代码分析
下面给出单例模式的实例代码
#include <iostream>
#include <string>
using std::string;
class Singleton : boost::noncopyable{
private:
string Name;
static Singleton *instance;
//保证外部无法创建实例
Singleton (const string& name) { Name = name;}
~Singleton(){}
public:
void whoAmI(){std::cout << "Name = " << Name << std::endl;}
static Singleton *getInstance(string name){
if (instance == NULL)
instance = new Singleton(name);
return instance;
}
};
int main(void){
Singleton *instance1 = Singleton::getInstance("instance1");
Singleton *instance2 = Singleton::getInstance("instance2");
Singleton *instance3 = Singleton::getInstance("instance3");
instance1.whoAmI();
instance2.whoAmI();
instance3.whoAmI();
return 0;
}
运行结果为:
Name = instance1
Name = instance1
Name = instance1
这里至关重要的技巧是将构造函数声明为私有,类的构造只能通过一个静态成员函数getInstance来构造,而该构造函数控制构造行为,达到构造唯一实例的目的
四、多线程环境下的单例模式
从上述代码中可以看到,我们利用一句if (instance == NULL)
来控制实例的创建,在多线程环境中,假设A线程正在创建实例的时候,B进程又调用了一个getInstance
,此时if (instance == NULL)
的判断依旧成立,从而创建又一个实例。在下面的测试中运行
int main(void){
Singleton *instance1, *instance2, *instance3;
pthread_t p1, p2, p3;
pthread_create(&p1, NULL, getSingleton1, &instance1);
pthread_create(&p2, NULL, getSingleton2, &instance2);
pthread_create(&p3, NULL, getSingleton3, &instance3);
pthread_join(p1, NULL);
pthread_join(p2, NULL);
pthread_join(p3, NULL);
instance1->whoAmI();
instance2->whoAmI();
instance3->whoAmI();
return 0;
}
运行结果为:
Name = instance1
Name = instance2
Name = instance3
所以单例模式必须要考虑线程同步,以下给出多线程版本
#include <iostream>
#include <string>
#include <pthread.h>
using std::string;
class Lock {
private:
pthread_mutex_t lock;
public:
Lock(){
pthread_mutex_init(&lock, NULL);
}
int getLock(){return pthread_mutex_lock(&lock);}
int unLock(){return pthread_mutex_unlock(&lock);}
virtual ~Lock(){ pthread_mutex_unlock(&lock);}
};
class Singleton : boost::nocopyable {
private:
string Name;
static Singleton *instance;
//保证外部无法创建实例
~Singleton(){}
Singleton (const string& name) { Name = name;}
public:
void whoAmI(){std::cout << "Name = " << Name << std::endl;}
static Singleton *getInstance(string name){
Lock lock;
lock.getLock();
if (instance == NULL)
instance = new Singleton(name);
lock.unLock();
return instance;
}
};
运行结果为:
Name = instance1
Name = instance1
Name = instance1
注:关于LINUX线程的文章请看我的另一篇博客《深入理解linux线程》