单例模式作为设计模式中最简单的一种,在面试中会被经常问道。在前人的基础上再做精简的总结:
https://blog.csdn.net/QIANGWEIYUAN/article/details/88544524
1.什么是单例模式
单例模式就是有这样一个类,无论你通过何种方式得到多少个该类的对象,结果是,所有的对象都指向该类的唯一一个对象。这就说明,单例模式只能实例化一个对象。我们怎么能做到这一点呢?
有这样一个解决方案:
- 在类中定义自己的唯一一个实例对象。
- 将构造函数私有化,使类不能在外部实例化。
- 通过一个静态的接口返回唯一的一个对象。
人们根据得到单例模式中唯一一个对象的时机不同,形象的将单例模式分为饿汉模式和懒汉模式。
2.饿汉模式
我们先来看一个简单的饿汉模式的代码:
# include<iostream>
using namespace std;
class Single
{
public:
static Single* GetInstance()
{
return &single;
}
private:
static Single single;
Single() { cout << "Construct Single" << endl; }
~Single() { cout << "Delete Single" << endl; }
Single(const Single& sing);
};
Single Single::single;//类外初始化
int main()
{
Single* p1 = Single::GetInstance();
Single* p2 = Single::GetInstance();
Single* p3 = Single::GetInstance();
cout << p1 << endl;
cout << p2 << endl;
cout << p3 << endl;
return 0;
}
如上述饿汉模式,无论程序用不用得到这个唯一的一个对象,都对这个唯一的对象进行实例化,同时给用户提供GetInstance()接口去得到该对象。
上述程序打印结果如下所示:
可以发现,三个指针指向的都是同一个对象。
3.懒汉模式
懒汉模式,顾名思义,就是,当程序需要这个对象的时候,再去进行实例化。
# include<iostream>
using namespace std;
class Single
{
public:
static Single* GetInstance()
{
if (single == NULL)
{
single = new Single();
}
return single;
}
private:
static Single* single;
Single() { cout << "Construct Single" << endl; }
~Single() { cout << "Delete Single" << endl; }
Single(const Single& sing);
//为了防止用户忘记delete,或者当多个指针指向对象的时候,delete 产生错误,自定义删除器
class Release
{
public:
~Release()
{
if (single != NULL)
{
delete single;
}
}
};
static Release release;
};
Single* Single::single = nullptr;//类外初始化为空
Single::Release Single::release;//利用对象的自动析构去释放单例对象
int main()
{
Single* p1 = Single::GetInstance();
Single* p2 = Single::GetInstance();
Single* p3 = Single::GetInstance();
cout << p1 << endl;
cout << p2 << endl;
cout << p3 << endl;
return 0;
}
运行结果如下:
在上述程序中,为了防止因为delete产生的相关的错误,我们自定义了一个内置类Release,利用自动析构的功能去delete在堆上申请的对象。
4.线程全的单例模式
如上所示,有饿汉模式和懒汉模式两种单例模式。
在饿汉模式中,对象在程序运行之前就已经创建好了,因此不存在线程安全的问题。
在懒汉单例模式中,获取对象的时候要先判断然后进行指针的判断,下来才是对象的内存申请。
single = new Single();这句代码先申请内存,然后调用Singlle的构造函数,最后将给single指针赋值。
那么如果有两个线程:假设为线程1和线程2:
假设线程1先调用GetInstance函数,single=NULL;会进入if语句,如果当线程用new开辟内存的时候,线程1的CPU时间片到了,那么这时线程调用GetInstance函数,也会进行new操作。那么这种情况就不再符合单例模式的要求了。
所以我们要对GetInstace()函数做加锁操作,防止多个线程同时都进入if条件语句的内部。
首先我们先封装一个锁:加头文件pthread.h
class Mutex
{
public:
Mutex()
{
cout << "construct mutex" << endl;
pthread_mutex_init(&mutex, NULL);
}
~Mutex()
{
cout << "destory mutex" << endl;
pthread_mutex_destroy(&mutex);
}
void Lock()
{
pthread_mutex_lock(&mutex);
}
void Unlock()
{
pthread_mutex_unlock(&mutex);
}
private:
pthread_mutex_t mutex;
};
同时为了提高效率,我们实现带有双重检验锁的线程安全的懒汉模式:
# include<iostream>
using namespace std;
# include<pthread.h>
class Mutex
{
public:
Mutex()
{
cout << "construct mutex" << endl;
pthread_mutex_init(&mutex, NULL);
}
~Mutex()
{
cout << "destory mutex" << endl;
pthread_mutex_destroy(&mutex);
}
void Lock()
{
pthread_mutex_lock(&mutex);
}
void Unlock()
{
pthread_mutex_unlock(&mutex);
}
private:
pthread_mutex_t mutex;
};
class Single
{
public:
static Single* GetInstance()
{
if (single == NULL)
{
mutex.Lock();
if (single == NULL)
{
single = new Single();
}
mutex.Unlock();
}
return single;
}
private:
static Single* single;
Single() { cout << "Construct Single" << endl; }
~Single() {
cout << "Delete Single" << endl;
}
Single(const Single& sing);
//为了防止用户忘记delete,或者当多个指针指向对象的时候,delete 产生错误,自定义删除器
class Release
{
public:
~Release()
{
if (single != NULL)
{
delete single;
}
}
};
static Release release;
static Mutex mutex;
};
Single* Single::single = nullptr;//类外初始化为空
Single::Release Single::release;//利用对象的自动析构去释放单例对象
Mutex Single::mutex;//互斥锁的类外初始化
int main()
{
Single* p1 = Single::GetInstance();
Single* p2 = Single::GetInstance();
Single* p3 = Single::GetInstance();
cout << p1 << endl;
cout << p2 << endl;
cout << p3 << endl;
return 0;
}
打印信息如下:
符合单例模式的要求:
5.一道面试题
在3中的懒汉模式中,如果我们将代码做如下修改,那么它是线程安全的吗?
# include<iostream>
using namespace std;
class Single
{
public:
static Single* GetInstance()
{
static Single single;
return &single;
}
private:
Single() { cout << "Construct Single" << endl; }
~Single() { cout << "Delete Single" << endl; }
Single(const Single& sing);
};
int main()
{
Single* p1 = Single::GetInstance();
Single* p2 = Single::GetInstance();
Single* p3 = Single::GetInstance();
cout << p1 << endl;
cout << p2 << endl;
cout << p3 << endl;
return 0;
}
上述单例模式在多线程环境中使用是完全可以的,因为系统对静态变量的初始化会自动进行加锁操作,使静态变量的初始化成为线程安全的操作,因此不会产生多个线程同时初始化single的情况。