首先说明:“singleton”是一个单词,不是一个复合词。
接下来步入正题。对于单例模式无论是“饿汉”还是“懒汉”都必须要有的是:
一.私有构造函数
二.声明静态单例对象
如果是“懒汉”的话为了写出好的没有 bug的单例代码还应注意
三.构造单例对象之前要加锁(lock一个静态的object对象)
四.需要两次检测单例实例是否已经被构造,分别在锁之前和锁之后
/*方法一:实现Singleton模式常规方法*/
class Singleton{
private:
Singleton() { } //关键点0:构造函数私有
static Singleton *p; //关键点1:声明单例对象是静态的
public:
static Singleton * instance();
};
Singleton * Singleton::instance() { //通过静态方法构造对象
if (p == NULL) //关键点2:判断单例对象是否被构造
p = new Singleton();
return p;
}
Singleton * Singleton::p=NULL; //注意!
分析:这段代码在通常情况下是可以运行的。但是遇到多线程可能就不行了。考虑两个线程同时调用instance方法且同时检测到p为NULL,显然此时就不是单例了。
既然说到了多线程,显然能想到加锁来处理。于是有:
//方法二:在常规常规方法基础上加锁
class Singleton {
private:
Singleton() { //关键点0:构造函数私有,对锁初始化。
pthread_mutex_init(&mutex,NULL);
}
static Singleton *p; //关键点1:声明单例对象是静态的
public:
static pthread_mutex_t mutex; //创建一个互斥锁
static Singleton *instance();
};
Singleton *Singleton::instance() {
if (p == NULL) { //关键点2:判断单例对象是否被构造
pthread_mutex_lock(&mutex); //关键点3:加线程锁
p = new Singleton;
pthread_mutex_unlock(&mutex); //解锁
}
return p;
}
Singleton* Singleton::p = NULL;
pthread_mutex_t Singleton::mutex;
分析:由于延迟加载或者缓存的原因,一次判断仍然不能保证只创建一个单例。于是乎有下面双重检查锁定模式。
//方法三:双重检查锁定模式
class Singleton {
private:
Singleton() { //关键点0:构造函数私有(并对锁初始化)。
pthread_mutex_init(&mutex, NULL);
}
static Singleton *p; //关键点1:声明单例对象是静态的
public:
static pthread_mutex_t mutex; //创建一个互斥锁
static Singleton *instance();
};
Singleton *Singleton::instance() {
if (p == NULL) { //关键点2:判断单例对象是否被构造
pthread_mutex_lock(&mutex); //关键点3:加线程锁
if (p == NULL) { //关键点4:二次判断是否已经初始化
p = new Singleton;
}
pthread_mutex_unlock(&mutex); //解锁
}
return p;
}
Singleton* Singleton::p = NULL;
pthread_mutex_t Singleton::mutex;
分析:至此代码就能确保单例,是可以接受的。但是这样的代码实现起来比较复杂,容易出错。
于是引出了“懒汉模式”和“饿汉模式”。
懒汉:万不得已才实例化类。延时加载——以时间换空间。
饿汉:一上来就把对象给new出来。非延时加载——以空间换时间
显然上面的代码都是“懒汉模式”,那嫩否给出一个“饿汉模式”的例子呢?当然//方法4:饿汉模式_这样所也不用加了。
/*人家还没要,自己就忍不住的先准备好了,如饥似渴,所以叫饿汉模式*/
class Singleton {
private:
Singleton() {} //关键点0:构造函数私有。
static Singleton* p; //关键点1:声明静态单例对象是
public:
static Singleton* instance() { //关键点3:需要的时候直接返回就好了。
return p;
}
};
Singleton* Singleton::p=new Singleton(); //关键点2:直接先创建了。
分析:代码非常短,效果非常好,连锁都不用了。
其实只要记住后面两个实现就可以了。如果更注重空间性能就选择“双重检查锁定”,如果更注重时间性能则果断选择“饿汉模式”。
https://www.jb51.net/article/100585.htm
http://blog.jobbole.com/109449/
https://www.cnblogs.com/dupengcheng/p/7205527.html