单例模式:
这是最简单的模式,就是在程序中,对于某个类只允许实例化一个对象。
单例类负责自己的实例化,并提供调用它的接口。
UML结构图
设计要点
- 构造函数Singleton()设计为private,这样就只能自己负责实例化自己。
- 定义一个private static Singleton(饿汉模式),或者定义一个private static Singleton * (懒汉模式)。这就要求在程序开始前生成一个Singleton,或者Singleton *
- 定义一个 static public 函数,(例如:GetInstance()),这是让外部对单例进行操作的接口。
下面的代码需要对static在类中的使用有一定的了解,请参考这个文章:https://blog.csdn.net/shaochuang1/article/details/96181430
代码
饿汉模式
#include <iostream>
using namespace std;
class Singleton
{
private:
Singleton(){
cout << "构造" << endl;
num = 10;
}
~Singleton(){
cout << "析构" << endl;
}
int num;
static Singleton locla_s;
public:
static Singleton *getInstance()
{
return &locla_s;
}
void Print()
{
std::cout << num << std::endl;
}
};
Singleton Singleton::locla_s;//值得注意的地方
int main()
{
Singleton* P_Singleton = Singleton::getInstance();
P_Singleton->Print();
return 0;
}
对于饿汉模式来说,程序一开始就需要将单例对象给实例化出来,无论我们需不需要。这对我们的内存是不友好的,如果当我们要用这个单例对象的时候,再来实例化它,就会很好。
懒汉模式
#include <iostream>
using namespace std;
class Singleton
{
private:
static Singleton *local_instance;
Singleton(){cout << "构造" << endl;}
~Singleton(){cout << "析构" << endl;}
public:
static Singleton *getInstance()
{
if (local_instance == nullptr)
{
local_instance = new Singleton();
cout << "new 新空间" << endl;
}
else
{
cout << "空间不变" << endl;
}
return local_instance;
}
static void delete_local()
{
delete local_instance;
local_instance = nullptr;
}
};
Singleton * Singleton::local_instance = nullptr;//值得注意
int main()
{
Singleton * s1 = Singleton::getInstance();
Singleton * s2 = Singleton::getInstance();
Singleton::delete_local();
return 0;
}
我们的懒汉模式就是在程序需要的时候,再来实例化单例对象。如果在多线程中这个懒汉模式也会出问题。如果两个线程同时构造单例对象,这个时候就会出现两个单例对象,这就违背了单例模式的设计原则啦。
适用于多线程中的懒汉模式
#include <iostream>
#include <mutex>
using namespace std;
class Singleton
{
private:
static Singleton *local_instance;
static mutex my_mutex;
Singleton(){cout << "构造" << endl;}
~Singleton(){cout << "析构" << endl;}
public:
static Singleton *getInstance()
{
std::lock_guard<std::mutex> my_lock_guard(my_mutex);
if (local_instance == nullptr)
{
local_instance = new Singleton();
cout << "new 新空间" << endl;
}
else
{
cout << "空间不变" << endl;
}
return local_instance;
}
static void delete_local()
{
delete local_instance;
local_instance = nullptr;
}
};
Singleton * Singleton::local_instance = nullptr;//值得注意
mutex Singleton::my_mutex;
int main()
{
Singleton * s1 = Singleton::getInstance();
Singleton * s2 = Singleton::getInstance();
Singleton::delete_local();
return 0;
}
通过观察你会发现,每次调用static Singleton *getInstance()这个函数的饿时候,都需要触发锁,但是实际情况是,只有第一次实例化单例类的时候才需要触发锁。每次都触发锁就使得代码的效率十分不好。这个时候我们就需要,双重锁定。
双重锁定
#include <iostream>
#include <mutex>
using namespace std;
class Singleton
{
private:
static Singleton *local_instance;
static mutex my_mutex;
Singleton(){cout << "构造" << endl;}
~Singleton(){cout << "析构" << endl;}
public:
static Singleton *getInstance()
{
if(local_instance == nullptr)
{
std::lock_guard<std::mutex> my_lock_guard(my_mutex);
if (local_instance == nullptr)
{
local_instance = new Singleton();
cout << "new 新空间" << endl;
}
}
else
{
cout << "空间不变" << endl;
}
return local_instance;
}
static void delete_local()
{
delete local_instance;
local_instance = nullptr;
}
};
Singleton * Singleton::local_instance = nullptr;//值得注意
mutex Singleton::my_mutex;
int main()
{
Singleton * s1 = Singleton::getInstance();
Singleton * s2 = Singleton::getInstance();
Singleton::delete_local();
return 0;
}
双重锁定的优点就是,只有第一次实例化单例类才需要触发锁,之后再调用这个函数就会被外层的if给屏蔽掉,大大提升了效率。