【设计模式】单例模式
一,定义
单例模式:创建型模式之一,是指在内存中只会创建且仅创建一次对象的设计模式。在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。
二,核心思想
只能有一个实例,有懒汉和饿汉区分,实现核心思想:
1.构造函数私有化
2.使用静态函数作为接口来获取类对象
三,分类
懒汉式
在真正需要使用对象时才去创建该单例类对象
1.1概念
懒汉式创建对象的方法是在程序使用对象前,先判断该对象是否已经实例化(判空),若已实例化直接返回该类对象,否则则先执行实例化操作。
1.2实现
//懒汉式
class Singleton_lazy
{
private:
Singleton_lazy() {}
Singleton_lazy(const Singleton_lazy&) = delete;
Singleton_lazy& operator=(const Singleton_lazy&) = delete;
public:
static Singleton_lazy* getInstance() //线程不安全
{
if (pSingleton == nullptr)
{
pSingleton = new Singleton_lazy;
}
return pSingleton;
}
private:
static Singleton_lazy* pSingleton;
};
Singleton_lazy* Singleton_lazy::pSingleton = nullptr;
1.3步骤总结
先将构造函数设置为私有属性,保证外界不能创建单例对象
删除拷贝构造,防止通过单例对象创建新对象
删除赋值重载,避免出现值语义的拷贝
定义 静态 私有 单例类指针对象
定义静态公有成员方法,保证外界可以获取单例对象,并在函数内动态构建单例对象
1.4问题
这样的写法会出现下面的问题:
1.5正确的懒汉式写法
为了解决线程不安全问题,我们需要对懒汉式的getInstance方法重写
使用 Double Check(双重校验) + Lock(加锁)
双重校验:
第一次if判断是为了提高代码效率
第二次if判断是为了防止别的线程进入到if和new之间,从而多次创建实例
class Singleton_lazy
{
private:
Singleton_lazy() {}
Singleton_lazy(const Singleton_lazy&) = delete;
Singleton_lazy& operator=(const Singleton_lazy&) = delete;
public:
static Singleton_lazy* getInstance()
{
if (pSingleton == nullptr) //第一次校验
{
mut.lock(); //加锁
if (pSingleton == nullptr) //第二次校验
{
pSingleton = new Singleton_lazy;
}
mut.unlock(); //解锁
}
return pSingleton;
}
private:
static Singleton_lazy* pSingleton;
static mutex mut; //定义锁
};
Singleton_lazy* Singleton_lazy::pSingleton = nullptr;
mutex Singleton_lazy::mut;
1.6优缺点
优点:
不执行getInstance()就不会被实例,资源利用率高
缺点:
第一次加载时不够快,需要使用额外资源(锁)
饿汉式
在类加载时,就已经创建好单例对象,等待被程序调用
2.1概念
饿汉式在类加载时已经创建好该对象,在程序调用时直接返回该单例对象。
即是程序开始之前在数据区创建一个静态对象,保证该单例对象的唯一性。
2.2实现
//饿汉式
class Singleton_hungry
{
private:
Singleton_hungry() {}
Singleton_hungry(const Singleton_hungry&) = delete;
Singleton_hungry& operator=(const Singleton_hungry&) = delete;
public:
static Singleton_hungry* getInstance()
{
return pSingleton;
}
private:
static Singleton_hungry* pSingleton;
};
Singleton_hungry* Singleton_hungry::pSingleton = new Singleton_hungry;
2.3步骤总结
先将构造函数设置为私有属性,保证外界不能创建单例对象
删除拷贝构造,防止通过单例对象创建新对象
删除赋值重载,避免出现值语义的拷贝
定义 静态 私有 单例类指针对象
定义静态公有成员方法,保证外界可以获取单例对象
在类外动态创建单例对象
2.4优缺点
优点:
线程安全
对象提前创建好,直接调用就可以,效率高
缺点:
提前创建对象会占用一定的内存空间
四,适用场景
需要频繁实例化适用然后销毁的对象
频繁访问数据库或文件的对象