1.含义
一个类只能实例化出一个对象
2.单例模式实现的两种方式
饿汉模式
我们在程序运行之初就将对象创建好,就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象。是以空间换时间的做法。这样程序运行中想要调用这个实例时都可以直接使用。举个例子:就像是一个人在吃完饭后立刻就去将碗洗了,这样以便于在下一次开饭的时候可以马上吃到饭。
实现原理:
为了在运行前就实例化好对象,并且不允许其他方式来实例化对象。那么我们首先将构造函数/拷贝构造函数/赋值运算符重载函数定义为私有。然后再在定义一个函数返回单例的地址。切记这个函数必须定义为静态的。倘若不设置成静态的,那么我们只有创建了对象才能调他,但是我们的初衷又是不要通过别的方式创建对象,这样就会产生矛盾。
饿汉模式的优点:简单
因为我们在程序启动时就设置好了单例,所以饿汉模式是线程安全的。
饿汉模式的缺点:
会拖慢程序启动的速度。
如果有多个单例类对象实例启动顺序不确定。
class Hungry
{
public:
static Hungry* GetInstance()//对外提供一个静态的成员函数,返回我们的单例
{
return &onlyInstance;
}
private:
static Hungry onlyInstance;//定义一个静态的实例
private://将其他可能产生实例的函数都弄成私有,设置为删除函数是C++11的新写法
Hungry() {};
Hungry(const Hungry&) = delete;
Hungry& operator=(const Hungry&) = delete;
};
Hungry Hungry::onlyInstance;//单例别忘了类外初始化
int main()
{
Hungry* a = Hungry::GetInstance();
}
懒汉模式
在用到的时候才去创建单例。例如:一个人吃完饭在,并不会直接将碗洗了,只有在下一次吃饭的时候才去洗碗
这种模式往往用在单利对象在创建时很耗时,占用资源,例如加载插件等等。这时候就会用到懒汉模式。
懒汉模式的优点:
第一次要使用单例的时候才去创建, 进程启动的时候无负载。
多个单例启动顺序可以自由控制多个单例启动顺序可以自由控制
懒汉模式缺点:代码复杂,需要考虑线程安全,由用户加锁
#include<iostream>
#include<mutex>
#include<thread>
using namespace std;
class Lazy
{
public:
static Lazy* GetInstance()//返回单例
{
if (OnlyInstance == nullptr)
{
m_mutex.lock();
if (OnlyInstance == nullptr)//?为什么要双层检查
{
OnlyInstance = new Lazy();
}
m_mutex.unlock();
}
return OnlyInstance;
}
private:
static Lazy* OnlyInstance;//单例指针
static mutex m_mutex;//互斥量
private:
Lazy() {};
Lazy(const Lazy&) {};
Lazy& operator=(const Lazy&) {};
};
Lazy* Lazy::OnlyInstance = nullptr;//初始化
mutex Lazy::m_mutex;
我们看到上边有层if判断那么为什么需要两层???
首先说第一层检查:
当一个线程运行到第一层检查,如果发现实例已经创建,就直接返回。
如果没有创建则通过第一层检查。
进入第一层检查后,我们让各个线程竞争锁,竞争到锁的线程(我们用A表示)来进行第二层检查
第二层检查:
当线程A获取锁后,再次判断,如果实例仍未被创建,则创建实例。如果实例已经被创建,那么就是上次拿到锁的线程(B)创建了实例(B创建的时候A一直阻塞在获得锁的地方,A并不知道B先于他获得了锁,如果不再次判断,那么仍然有可能创建多个实例)。返回即可。