【设计模式】单例模式

一,定义

单例模式:创建型模式之一,是指在内存中只会创建且仅创建一次对象的设计模式。在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。

二,核心思想

只能有一个实例,有懒汉和饿汉区分,实现核心思想:

1.构造函数私有化

2.使用静态函数作为接口来获取类对象

三,分类

  1. 懒汉式

  • 真正需要使用对象时才去创建该单例类对象

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()就不会被实例,资源利用率高

缺点

  • 第一次加载时不够快,需要使用额外资源(锁)

  1. 饿汉式

  • 类加载时,就已经创建好单例对象,等待被程序调用

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优缺点

优点:

  • 线程安全

  • 对象提前创建好,直接调用就可以,效率高

缺点:

  • 提前创建对象会占用一定的内存空间

四,适用场景

  1. 需要频繁实例化适用然后销毁的对象

  1. 频繁访问数据库或文件的对象

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逐梦的白鹰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值