设计模式——针对典型场景所设计出来的特别的处理方案
单例模式:一个类只能实例化一个对象(所以叫单例)
场景:
1、资源角度:资源在内存中只占有一份
2、数据角度:如果只有一个对象,那么该对象在什么时候访问它的资源都是一致的
目录
实现方式
一、饿汉模式
二、懒汉模式
就比如任务管理器,我再怎么重复打开它,它也只有这一份任务管理器。
实现方式
一、饿汉模式
所谓饿汉模式,直接将对象实例化完毕,用的时候直接就使用了(空间换时间)
好处:效率最大化,资源用的时候直接就用了(不涉及线程安全问题)
缺点:初始化速度慢,占用内存多
实现:使用静态成员变量(保证它在初始化就能有一份内存)
将构造函数私有化(类外无法实例化对象,对象只能有一份)
提供public接口进行访问
#include <iostream>
class Singleton
{
private:
int _data;
private:
static Singleton eton;
Singleton() // 将构造函数私有化,意味着在类外无法实例化对象
{ // 保证一个类只能实例化一个对象(构造函数私有化,该类也无法被继承)
// 逻辑处理begin
_data = 100;
// 逻辑处理end
}
public:
static Singleton *GetInstence() // 静态成员只能被静态成员函数调用
{
return &eton;
}
int Get_data() // 提供一个获取data的方法
{
return _data;
}
};
// 静态成员变量,类内声明、类外定义
Singleton Singleton::eton;
int main()
{
Singleton *eton = Singleton::GetInstence();
std::cout << eton->Get_data() << std::endl;
return 0;
}
二、懒汉模式
所谓懒汉模式,就是等待用的时候才去进行构造,才去进行实例化
优点:内存资源占用少,初始化效率高
缺点:需要访问资源的时候需要去实例化对象加载(第一次访问时对象时比较慢)(涉及线程安全问题)
实现:
1、构造函数私有化(保证只能实例化一个对象)
2、定义静态类型的指针,在用的时候进行new
3、new的时候注意线程安全问题(加锁保护)
4、避免已经实例化之后后续资源重复加解锁访问(double check)
5、使用volatile修饰,避免编译器过度优化
volatile:当一个变量经常被cpu访问,那就把这个变量放入一个寄存器中,这样对于这样一个高频访问的变量,cpu就可以直接从寄存器中访问,避免了来回从内存中拿取数据。
#include <iostream>
#include <mutex>
class Singleton
{
private:
int _data;
private:
static Singleton *_eton; // 1、定义静态的指针与锁
static std::mutex _mutex;
Singleton() /// 2、构造函数私有化(只能被实例化一次)
{
// 逻辑处理begin
_data = 100;
// 逻辑处理end
}
public:
int get_data() { return _data; }
static Singleton *GetInsetnce()
{
if (_eton == nullptr) // 5、double check避免了已经实例化了还需要加解锁问题
{
_mutex.lock(); /// 4、使用mutex进行安全控制
if (_eton == nullptr)
_eton = new Singleton; // 3、等到用的时候进行new 调用构造实例化
_mutex.unlock();
}
return _eton;
}
};
// 6、对其构造和声明使用volatile修饰 保证数据的可见性
// volatile Singleton *Singleton::_eton = nullptr;
Singleton *Singleton::_eton = nullptr;
std::mutex Singleton::_mutex;
int main()
{
Singleton *eton = Singleton::GetInsetnce();
std::cout << eton->get_data() << std::endl;
}