单例模式使用是最高的
关键字 “单” ,就是对象唯一 , 对象属于类的,类在实例化的时候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;
}