什么是单例模式
应该怎样去创建一个唯一的变量或对象?在基于对象的设计中我们可以通过创建一个全局变量或对象来实现,在面向对象和面向过程结合的设计范式(如C++中) 中,我们也还是可以通过一个全局变量实现这一点。 但是当我们遇到了纯粹的面向对象范式(Java)中,这一点可能就只能是通过Singleton模式来实现了。
实现
通过维护一个static
的成员变量来记录这个唯一的对象实例。通过提供一个staitc的接口instance来获得这个唯一的实例。因为都是static
,所以这些类中的对象或者接口都是其实例化的所有对象公共拥有的唯一一份。
1、线程不安全的单例,且在对象生命周期过后,对象并没有析构。
/*
线程不安全的单例,且在对象生存期到时候不会被析构。
*/
class Singleton{
public:
static Singleton *GetInstance(){//静态函数,提供全局访问点
if(instance_ == NULL){
instance_ = new Singleton();
}
return instance_;
}
private:
Singleton(){//私有化默认构造函数,只能在GetInstance中调动,外部不能new对象。
cout << "Singleton Init..." << endl;
}
Singleton(const Singleton &rhs);//禁止拷贝构造 a(b)这种形式
Singleton &operator=(const Singleton &rhs);//禁止赋值运算符 a = b这种形式
static Singleton *instance_;//静态对象
};
Singleton *Singleton::instance_ = NULL;//静态成员初始化
int main(void){
Singleton *s1 = Singleton::GetInstance();
Singleton *s2 = Singleton::GetInstance();//静态可以直接获取
return 0;
}//仅仅输出一个Singleton Init...
Singleton单例类特征
- 它有一个指向唯一实例的静态指针instance_,并且是私有的;
- 它有一个公有的函数,可以获取这个唯一的实例,并且在需要的时候创建该实例
- 它的构造函数是私有的,这样就不能从别处创建该类的实例。
- 拷贝构造函数和赋值运算符都私有,使得不允许拷贝和赋值。
大多数时候,这样的实现都不会出现问题。有经验的读者可能会问,instance_指向的空间什么时候释放呢?更严重的问题是,该实例的析构函数什么时候执行?
如果在类的析构行为中有必须的操作,比如关闭文件,释放外部资源,那么上面的代码无法实现这个要求。我们需要一种方法,正常的删除该实例。
程序在结束的时候,系统会自动析构所有的全局变量。事实上,系统也会析构所有的类的静态成员变量,就像这些静态成员也是全局变量一样。利用这个特征,我们可以在单例类中定义一个这样的静态成员变量,而它的唯一工作就是在析构函数中删除单例类的实例。如下面的代码中的CGarbo类(Garbo意为垃圾工人)
2、线程不安全的单例,且在对象生命周期过后,对象析构。
class Singleton{
public:
static Singleton *GetInstance(){//静态函数,提供全局访问点
if(instance_ == NULL){
instance_ = new Singleton();
}
return instance_;
}
~Singleton(){
cout << "~Singleton..." << endl;
}
class Garbo{//辅助类,主要用其析构函数删除实例对象
public:
~Garbo(){
if(instance_ != NULL)
delete instance_;
}
};
private:
Singleton(){//私有化默认构造函数,只能在GetInstance中调动,外部不能new对象。
cout << "Singleton Init..." << endl;
}
Singleton(const Singleton &rhs);//禁止拷贝构造 a(b)这种形式
Singleton &operator=(const Singleton &rhs);//禁止赋值运算符 a = b这种形式
static Singleton *instance_;//静态对象
static Garbo garbo_;//利用对象确定性析构
};
Singleton *Singleton::instance_ = NULL;//静态成员初始化
Singleton::Garbo Singleton::garbo_;//静态默认初始化
int main(void){
Singleton *s1 = Singleton::GetInstance();
Singleton *s2 = Singleton::GetInstance();//静态可以直接获取
return 0;
}
利用静态嵌套对象的确定性析构会调用Garbo类的析构函数,在析构函数内delete单例类的指针。
3、上述析构过于麻烦可以使用局部静态对象的引用解决
局部静态对象仅仅初始化一次存储在数据区,并且在程序退出的时候,会自动析构局部对象。所以完全满足单例的全部条件。
class Singleton{
public:
static Singleton &GetInstance(){//静态函数,提供全局访问点
static Singleton instance_;//静态局部对象,仅仅初始化一次
return instance_;
}
~Singleton(){
cout << "~Singleton..." << endl;
}
private:
Singleton(){//私有化默认构造函数,只能在GetInstance中调动,外部不能new对象。
cout << "Singleton Init..." << endl;
}
Singleton(const Singleton &rhs);//禁止拷贝构造 a(b)这种形式
Singleton &operator=(const Singleton &rhs);//禁止赋值运算符 a = b这种形式
};
int main(void){
Singleton &s1 = Singleton::GetInstance();
Singleton &s2 = Singleton::GetInstance();//静态可以直接获取,系统最后会自动调用析构函数
return 0;
}
4、线程安全的单例
只有在对象创建的时候,才有可能出现线程不安全。两个线程同时竞争创建。于是有了下面两种解决线程安全的问题。
1、饿汉模式(在某些场景下受到限制)
在main函数开始的时候即创建对象,其他就是读取对象了,那么饿汉模式是线程安全的。
2、懒汉模式(适用于各种场景),使用互斥锁(一种使用原始锁,二种就是使用RALL手法)
直接使用互斥量,并且采用了atexit,注册在程序结束的时候,调用析构函数。
class Singleton{
public:
static Singleton *GetInstance(){//静态函数,提供全局访问点
if(instance_ == NULL){
pthread_mutex_lock(&lock);
instance_ = new Singleton();
atexit(Destory);//注册程序结束的时候,调用Destory析构对象,避免了再次使用garbo类
pthread_mutex_unlock(&lock);
}
return instance_;
}
~Singleton(){
cout << "~Singleton..." << endl;
}
private:
Singleton(){//私有化默认构造函数,只能在GetInstance中调动,外部不能new对象。
cout << "Singleton Init..." << endl;
}
Singleton(const Singleton &rhs);//禁止拷贝构造 a(b)这种形式
Singleton &operator=(const Singleton &rhs);//禁止赋值运算符 a = b这种形式
static void Destory(){
delete instance_;
}
static Singleton *instance_;//静态对象
static pthread_mutex_t lock;//静态锁对象
};
pthread_mutex_t Singleton::lock = PTHREAD_MUTEX_INITIALIZER;//初始化锁
Singleton *Singleton::instance_ = NULL;//静态成员初始化
int main(void){
Singleton *s1 = Singleton::GetInstance();
Singleton *s2 = Singleton::GetInstance();//静态可以直接获取,系统最后会自动调用析构函数
return 0;
}
将锁使用RALL手法封装起来,简化锁的使用。
/*
简单封装Mutex的构造和析构、加锁和解锁
*/
#include<pthread.h>
#include<iostream>
using namespace std;
class MutexLock
{
public:
MutexLock()
{
pthread_mutex_init(&mutex_, NULL);
}
~MutexLock()
{
pthread_mutex_destroy(&mutex_);
}
void lock()
{
pthread_mutex_lock(&mutex_);
}
void unlock()
{
pthread_mutex_unlock(&mutex_);
}
private:
MutexLock(const MutexLock&);//禁止拷贝
MutexLock& operator=(const MutexLock&);//禁止赋值
pthread_mutex_t mutex_;//锁
};
/*
通过引用将RALL手法封装锁
*/
class MutexLockGuard
{
public:
explicit MutexLockGuard(MutexLock& mutex):mutex_(mutex)//构造加锁,且禁止通过此构造函数类型转换
{
mutex_.lock();
}
~MutexLockGuard()//析构释放锁
{
mutex_.unlock();
}
private:
MutexLockGuard(const MutexLockGuard&);//禁止拷贝
MutexLockGuard& operator=(const MutexLockGuard&);//禁止赋值
MutexLock& mutex_;//定义一个锁的引用
};
#define MutexLockGuard(x) error "Missing guard object name"//防止产生匿名对象,生命周期非常短,没有锁住。
//防止产生临时对象
class Singleton{
public:
static Singleton *GetInstance(){//静态函数,提供全局访问点
if(instance_ == NULL){
//MutexLockGuard lock(Singleton::mutex);//创建一个匿名对象,并通过mutex_初始化,析构
MutexLockGuard lock(mutex);
instance_ = new Singleton();
atexit(Destory);//注册程序结束的时候,调用Destory析构对象,避免了再次使用garbo类
}
return instance_;
}
~Singleton(){
cout << "~Singleton..." << endl;
}
private:
Singleton(){//私有化默认构造函数,只能在GetInstance中调动,外部不能new对象。
cout << "Singleton Init..." << endl;
}
Singleton(const Singleton &rhs);//禁止拷贝构造 a(b)这种形式
Singleton &operator=(const Singleton &rhs);//禁止赋值运算符 a = b这种形式
static void Destory(){
delete instance_;
}
static Singleton *instance_;//静态对象
static MutexLock mutex;//互斥锁,此时没有初始化,需要在下面初始化 调用了默认构造函数
};
/*
静态变量必须初始化
*/
Singleton *Singleton::instance_ = NULL;//静态成员初始化
MutexLock Singleton::mutex;//相当于调用默认构造函数初始化
int main(void){
Singleton *s1 = Singleton::GetInstance();
Singleton *s2 = Singleton::GetInstance();//静态可以直接获取,系统最后会自动调用析构函数
return 0;
}
时刻注意静态对象必须初始化,才会调用其默认构造函数。
4、线程安全的单例模板
/*
简单封装Mutex的构造和析构、加锁和解锁
*/
#include<pthread.h>
#include<iostream>
using namespace std;
class MutexLock
{
public:
MutexLock()
{
pthread_mutex_init(&mutex_, NULL);
}
~MutexLock()
{
pthread_mutex_destroy(&mutex_);
}
void lock()
{
pthread_mutex_lock(&mutex_);
}
void unlock()
{
pthread_mutex_unlock(&mutex_);
}
private:
MutexLock(const MutexLock&);//禁止拷贝
MutexLock& operator=(const MutexLock&);//禁止赋值
pthread_mutex_t mutex_;//锁
};
/*
通过引用将RALL手法封装锁
*/
class MutexLockGuard
{
public:
explicit MutexLockGuard(MutexLock& mutex):mutex_(mutex)//构造加锁,且禁止通过此构造函数类型转换
{
mutex_.lock();
}
~MutexLockGuard()//析构释放锁
{
mutex_.unlock();
}
private:
MutexLockGuard(const MutexLockGuard&);//禁止拷贝
MutexLockGuard& operator=(const MutexLockGuard&);//禁止赋值
MutexLock& mutex_;//定义一个锁的引用
};
#define MutexLockGuard(x) error "Missing guard object name"//防止产生匿名对象,生命周期非常短,没有锁住。
//防止产生临时对象
class test{
public:
test(){
cout << "test.." << endl;
}
~test(){
cout << "~test.." << endl;
}
};
template <typename T>
class Singleton{
public:
static T *GetInstance(){//静态函数,提供全局访问点
if(instance_ == NULL){
//MutexLockGuard lock(Singleton::mutex);//创建一个匿名对象,并通过mutex_初始化,析构
MutexLockGuard lock(mutex);
instance_ = new T();
atexit(Destory);//注册程序结束的时候,调用Destory析构对象,避免了再次使用garbo类
}
return instance_;
}
~Singleton(){
cout << "~Singleton..." << endl;
}
private:
Singleton(){//私有化默认构造函数,只能在GetInstance中调动,外部不能new对象。
cout << "Singleton Init..." << endl;
}
Singleton(const Singleton &rhs);//禁止拷贝构造 a(b)这种形式
Singleton &operator=(const Singleton &rhs);//禁止赋值运算符 a = b这种形式
static void Destory(){
delete instance_;
}
static T *instance_;//静态对象
static MutexLock mutex;//互斥锁,此时没有初始化,需要在下面初始化 调用了默认构造函数
};
/*
静态变量必须初始化
*/
template <typename T>
T *Singleton<T>::instance_ = NULL;//静态成员初始化
template <typename T>
MutexLock Singleton<T>::mutex;//相当于调用默认构造函数初始化
int main(void){
Singleton<test>::GetInstance();
Singleton<test>::GetInstance();
return 0;
}