单例模式是常考考点之一,盘一下。
1.单例模式
保证一个类仅有一个实例,提供一个全局访问点。
有饿汉式与懒汉式之分:
饿汉式:在初始化时就实例化
懒汉式:在第一次被引用时才实例化,即第一次调用getTest()时实例化
2.具体代码
总体而言的编码特征为:
私有:对象的指针、构造函数、[互斥锁]
公有:getTest()、[析构函数]
【注意:对象的指针、互斥锁、getTest均为static的】
具体实现主要有三种,本篇盘的均为懒汉式的:
(1)普通有缺陷的单例模式
在多线程场景下可能会多次new:例如线程1判断到t == nullptr,于是进去执行new,但是在线程1 new出来对象之前线程2判断到t == nullptr,于是也进去new。
没有析构函数的释放,造成内存泄漏。
//1.普通单例
class Test
{
private:
static Test* t;
Test()
{
cout<<"con"<<endl;
}
public:
static Test* getTest()
{
if(t == nullptr)
{
t = new Test();
}
return t;
}
};
Test* Test::t = nullptr;//不要忘记初始化
(2)双重锁定的单例模式
使用双重锁定 + 互斥锁 + 智能指针解决(1)中的两个缺点
//2.双重锁定单例 智能指针+双重锁定+互斥锁
#include <mutex>
#include <memory>
class Test_2
{
private:
static shared_ptr<Test_2> t;//注意
static mutex mut;//注意
Test_2()
{
cout<<"con_2"<<endl;
}
public:
~Test_2()
{
cout<<"decon_2"<<endl;
}
static shared_ptr<Test_2> getTest()
{
if(t == nullptr)
{
lock_guard<mutex> lock(mut);
if(t == nullptr)
{
t = shared_ptr<Test_2>(new Test_2);
}
}
return t;
}
};
shared_ptr<Test_2> Test_2::t = nullptr;
mutex Test_2::mut;
但是有些平台双重锁定(两个if)会失效,同时代码量也不小。
(3)局部静态变量的单例模式
此方法为最佳,即减少了代码量,又因为在初始化static变量时,并发进入声明语句会导致线程阻塞直到初始化结束,所以保证线程获取到的实例一定是初始化了的。
//3.局部静态变量
class Test_3
{
private:
Test_3()
{
cout<<"con_3"<<endl;
}
public:
~Test_3()
{
cout<<"decon_3"<<endl;
}
static Test_3& getTest()//注意返回值类型
{
static Test_3 t;
return t;
}
};
int main()
{
Test& t = Test_3::getTest();//注意是引用类型
}