创建型模式1-单例模式和任务队列

单例模式使用是最高的

关键字 “单” ,就是对象唯一 ,  对象属于类的,类在实例化的时候new 出来一个唯一的实例化对象  这种叫做单例类(对应的模式就是单例模式),单例模式就是内的对象只能创建一个。

队列:(先进先出)fi(before in)fo(before out),吃饭先,上厕所后。栈 :先进后出。

任务队列:任务对应的是任务函数,给任务队列存储的是任务函数的地址(就是函数的名字),存储的时候可以在 队列里面的节点  定义一些函数指针 用于存储函数地址。

C++11里面有两种处理任务队列的方式:1、函数指针。2、可调用对象:在任务队列里储存一个可调用对象,可调用对象发生器。

在一个项目中,全局范围内,某个类的实例有且仅有一个,通过这个唯一实例向其他模块提供数据的全局访问,这种模式就叫单例模式。单例模式的典型应用就是任务队列。

这么做才能保证这个对象唯一? 独生 想生成多个对象都不行。

《***饿汉模式-----定义类的时候创建单例对象*********》

QTaskQuen.cpp文件

#include "QTaskQuen.h"


int main()
{
    QTaskQuen* taskQ = QTaskQuen::getInstance();
    taskQ->print();
    return 0;

    //总结:单例模式就是不让 用户在内的外面去调用他的构造函数。
    //包或它自动默认的构造,或者拷贝够很,或者移动构造 

   // 单例模式就是一个静态对象。


}

QTaskQuen.h文件

#pragma once
#include <iostream>
using namespace std;
//单例模式任务队列

//那些构造函数会影响创建多过构造函数的
class QTaskQuen
{   

    //1、 默认构造函数没有参数的(可以创建多个对象)
    //2、 拷贝构造(可以创建多对象实例)
    //3、移动构造函数(不会创建多个对象)
    
    //所有要保证 1、2 不能够创建多个实例
    /*
    方法一: 设置为私有的, 不行,外面类不能访问

    方法二: 用显式的地把这两个  构造函数删除 
    */
public:
    //QTaskQuen() = delete;
    QTaskQuen(const QTaskQuen & t) = delete;
    QTaskQuen& operator=(const QTaskQuen& t) = delete;
    //把它删除后已经无法再创建对象。
    
    //想得到对象需要通过类名访问用静态属性和方法。
    static QTaskQuen* getInstance() 
    {
        return m_taskQ;
    }
    void print() 
    {
        printf_s("我是单例模式一个成员函数");
    }

private:
    //方法一:C++ 特性 //默认
    QTaskQuen() = default;   
    //QTaskQuen(const QTaskQuen & t) = default;
    //QTaskQuen& operator=(const QTaskQuen& t) = delete;
    
    static QTaskQuen* m_taskQ;//想操作静态成员变量,需要静态成员函数操作
    
    /*是静态成员函数只能操作静态成员变量*/
    //static QTaskQuen* m_taskQ; //必须在类的外面做初始化,有且只有一个该对象变量。

};

// new QTaskQuen;会报错   他已经是被删除的构造函数。
//除非删除 QTaskQuen() = delete,打开QTaskQuen() = default;   
QTaskQuen*  QTaskQuen::m_taskQ = new QTaskQuen;  //定义完类了就立即new 对象 出来就急切要使用所有叫饿汉模式

<-------------*******这里饿汉模式例子结束----------*******>

*****************以下懒汉模式******************************

懒汉模式-> 什么时候使用这个单例对象,在使用的时候再去创建对应的实例

相对饿汉比较省空间:因为饿汉模式一开始就会占用空间,饿懒汉模式需要用的时候才去创建。

class QTaskQuen
{   
public:
    //QTaskQuen() = delete;
    QTaskQuen(const QTaskQuen & t) = delete;
    QTaskQuen& operator=(const QTaskQuen& t) = delete;
 
    static QTaskQuen* getInstance() 
    {

     if(m_taskQ == nullptr)

{

  m_taskQ == new QTaskQuen;

}
        return m_taskQ;
    }
    void print() 
    {
        printf_s("我是单例模式一个成员函数懒汉模式");
    }

private:
    //方法一:C++ 特性 //默认
    QTaskQuen() = default;   
    //QTaskQuen(const QTaskQuen & t) = default;
    //QTaskQuen& operator=(const QTaskQuen& t) = delete;    
    static QTaskQuen* m_taskQ;//想操作静态成员变量,需要静态成员函数操作

};
QTaskQuen*  QTaskQuen::m_taskQ = new QTaskQuen; 

<<<<<<<<<<<<<<<------懒汉模式结束--------->>>>>>>>>>>>>>>>>>>

以上都是在单线程的时候情况下的

但是在多线程的场景下, 饿汉模式是安全的,线程安全的意思就是多线程可以同时访问这个单例的对象,多线程下使用懒汉模式 多线程同时访问getInstance() 时候 会同时产生多个 访问m_taskQ 会有线程安全问题(加了判断也有,因为可以有多个线程在同一时间经过开),可以加互斥锁把这个线程阻塞,让多个线程依次访问这个m_taskQ这个实例,多线程下使用饿汉模式线程安全,而且效率会高一点。类的静态成员变量在调用的时候只会初始化一次,所以饿汉模式在多线程种可能是安全的。

#include <mutex>

static QTaskQuen* getInstance()
    {
        m_mutex.lock();
        if (m_taskQ == nullptr) 
        {
            m_taskQ = new QTaskQuen;
        }
        m_mutex.unlock();
        return m_taskQ;
    }

加锁之后没有线程安全问题但是效率会变低。

解决效率问题可以使用双重检查锁定解决安全问题,多线程 第一次效率没有改变,第二次就如果拿到 对象的时候就不进去了

static QTaskQuen* getInstance()
    {
        if (m_taskQ == nullptr) //双重检查锁定解决线程效率问题
        {
            m_mutex.lock(); //加锁解决 线程安全问题  
            if (m_taskQ == nullptr)
            {
                m_taskQ = new QTaskQuen;
            }
            m_mutex.unlock();
        }
        return m_taskQ;
    }

运行时如果遇到这个错误:严重性    代码    说明    项目    文件    行    禁止显示状态
错误    LNK2001    无法解析的外部符号 "private: static class std::mutex QTaskQuen::m_mutex" (?m_mutex@QTaskQuen@@0Vmutex@std@@A)    单例模式    D:\MFC\单例模式\单例模式\QTaskQuen.obj    1    

要在 类外 声明一下:

//它是静态成员,但是不需要初始化话,本来静态的都需要再类外初始化。

mutex TaskQueue::m_mtex;

使用原子变量解决双重检查锁定的问题

存在双重锁定的问题漏洞: 机器底层操作时候m_taskQ = new QTaskQuen这一句其实有3个操作,这一句底层的机器指令有可能会被打破顺序。

解决方法:C++ 11 atomic可以指定机器执行的顺序

#include <atomic>

static atomic <TaskQueue*> :: m_taskQ;

atomic <TaskQueue*>  TaskQueue::m_taskQ ;

//原子锁解决方案

static QTaskQuen* getInstance()
    {

   QTaskQuen* task = m_taskQ .load();
        if (task== nullptr) 
        {
            m_mutex.lock();

           task = m_taskQ .load();
            if (task== nullptr)
            {
                task= new QTaskQuen;

             task.store(task);
            }
            m_mutex.unlock();
        }
        return task;
    }

懒汉模式下使用局部静态对象解决线程安全问题--前提是编译器支持C++ 11

static QTaskQuen* getInstance()
    {

   static  QTaskQuen task;

  return &task;
    }


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值