【设计模式】创建型模式01:单例模式
单例模式OVERVIOW单例模式1.单例模式实现2.饿汉与懒汉1饿汉模式2懒汉模式3.懒汉线程安全-处理方式11引入互斥锁2引入双重检查锁定3引入原子变量4.懒汉线程安全-处理方式21设置局部静态对象5.案例运用1任务队列简单实现2用户登录项目全局范围内某个类的实例有且仅有一个通过这个实例向其他模块提供数据的全局访问这种模式就叫单例模式。单例模式的典型应用就是任务队列。使用单例模式来替代全局变量对全局变量进行管理直接使用全局变量会破坏类的封装全局变量随意读写通过单例模式的类提供的成员函数进行访问。单例模式优点提高性能避免频繁的创建销毁对象提高性能节省内存空间在内存中只有一个对象节省内存空间避免多重占用避免对共享资源的多重占用全局访问可全局访问利用单例模式避免全局变量的出现单例模式缺点扩展困难单例模式中没有抽象层因此扩展困难不适用于变化的对象如果同类型的对象总是要在不同的用例场景发生变化单例就会引起数据错误不能保存状态。职责过重违背了单一职责原则负面问题为了节省资源将数据库连接池对象设计为单例类可能会导致共享连接池对象的程序过多而出现连接池溢出。如果实例化的单例对象长时间不被利用系统会认为是垃圾而被回收这将导致对象状态的丢失。单利模式使用场景需要频繁实例化然后销毁的对象创建对象耗时过多or消耗资源过多但又经常使用到的对象有状态的工具类对象频繁访问数据库或文件的对象要求只有一个对象的场景1.单例模式实现如果使用单例模式首先要保证这个类的实例有且仅有一个。因此就必须采取一些操作涉及一个类多对象操作的函数有以下几个构造函数创建一个新的对象拷贝构造函数根据已有对象拷贝出一个新的对象拷贝赋值操作符重载函数两个对象之间的赋值为了把一个类可以实例化多个对象的路堵死需要对以上几个函数做如下处理构造函数私有化在类内部只调用一次这是可控的。由于类外部不能使用构造函数所以在类内部创建的唯一的对象必须是静态的这样就可以通过类名来访问了为了不破坏类的封装把这个静态对象设置为私有。在类中只有它的静态成员函数才能访问其静态成员变量所以给这个单例类提供一个静态函数用于得到这个静态的单例对象。拷贝构造函数私有化或者禁用使用 delete拷贝赋值操作符重载函数私有化或者禁用从单例的语义上讲该函数已经毫无意义所以在类中不再提供这样一个函数故将它也一并处理单例模式就是给类创建一个唯一的实例对象UML类图如下#includeiostreamusingnamespacestd;/* 1.关于类创建后的默认提供的函数 - 在创建一个新的类之后 会默认提供3个构造函数 1个析构函数 - 2个操作符重载移动赋值操作符重载、拷贝赋值操作符重载移动构造函数 拷贝构造函数 2.关于单例模式下类的实例化 - 在通过将 无参构造函数、拷贝构造函数、拷贝赋值操作符重载函数禁用之后 TaskQueue类已经无法在外部创建任何的对象 - 要得到TaskQueue的实例无法通过new操作符得到 只能通过类名得到需要将对象设置为静态对象 - 通过类名访问类内部的属性和方法 其属性和方法一定是静态的若不是静态需要通过对象来调用 - 能够操作静态成员变量的函数 只有静态成员函数 *///单例模式任务队列classTaskQueue{public://无参构造函数//TaskQueue() delete;//拷贝构造函数TaskQueue(constTaskQueuet)delete;//赋值操作符重载函数TaskQueueoperator(constTaskQueuet)delete;// delete 代表函数禁用, 也可以将其访问权限设置为私有//静态成员公共函数用于获取实例staticTaskQueue*getInstance(){returnm_taskq;}voidprintTest(){couti am a public method of a singleton classendl;}private://无参构造函数TaskQueue()default;//拷贝构造函数//TaskQueue(const TaskQueue t) default;//赋值操作符重载函数//TaskQueue operator(const TaskQueue t) default;//通过类名访问静态属性或方法来创建类实例需要在类外部做初始化处理staticTaskQueue*m_taskq;};//静态成员初始化放到类外部处理TaskQueue*TaskQueue::m_taskqnewTaskQueue;intmain(){//获取TaskQueue的单例对象 由m_taskq指针指向TaskQueue*m_taskqTaskQueue::getInstance();//由m_taskq指针调用单例类内部的成员方法m_taskq-printTest();return0;}以上为单例模式中的饿汉模式在定义单例类的时候就将类对应的单例对象一并创建出来了。2.饿汉与懒汉在实现一个单例模式的类的时候有两种处理模式饿汉模式在将单例类定义出来后实例就已经存在了饿汉模式没有线程安全问题懒汉模式在使用单例对象的时候才会去创建单例对象的实例节省内存空间懒汉模式存在线程安全问题多个线程同时访问单例的实例1饿汉模式多个线程在访问单例对象时没有线程安全问题单例对象已经存在不会出现多个线程创建出多个单例对象的情况。多线程拿到单例对象后在访问单例对象内部的数据时有线程安全问题多线程共享资源//饿汉模式classTaskQueue{public:TaskQueue(constTaskQueuet)delete;TaskQueueoperator(constTaskQueuet)delete;staticTaskQueue*getInstance(){returnm_taskq;}voidprintTest(){couti am a public method of a singleton classendl;}private:TaskQueue()default;staticTaskQueue*m_taskq;};TaskQueue*TaskQueue::m_taskqnewTaskQueue;//饿汉模式classTaskQueue{public:TaskQueue(constTaskQueuet)delete;TaskQueueoperator(constTaskQueuet)delete;staticTaskQueue*getInstance(){returnm_taskq;}voidprintTest(){couti am a public method of a singleton classendl;}private:TaskQueue()default;staticTaskQueue m_taskq;//已经创建对象};TaskQueue*TaskQueue::m_taskq;//改为对象声明2懒汉模式//懒汉模式classTaskQueue{public:TaskQueue(constTaskQueuet)delete;TaskQueueoperator(constTaskQueuet)delete;staticTaskQueue*getInstance(){if(m_taskqnullptr)m_taskqnewTaskQueue;returnm_taskq;}voidprintTest(){couti am a public method of a singleton classendl;}private:TaskQueue()default;staticTaskQueue*m_taskq;};TaskQueue*TaskQueue::m_taskqnullptr;3.懒汉线程安全-处理方式1在单例模式中饿汉模式下针对在多线程中可能存在的线程安全问题创建多个实例进行问题修改1引入互斥锁在多线程环境下有可能的情况是多个线程同时进入到getInstance()方法中的if语句判断中这时对象就可能被同时创建多个//懒汉模式 引入互斥锁classTaskQueue{public:TaskQueue(constTaskQueuet)delete;TaskQueueoperator(constTaskQueuet)delete;staticTaskQueue*getInstance(){m_mutex.lock();if(m_taskqnullptr)m_taskqnewTaskQueue;m_mutex.unlock();returnm_taskq;}voidprintTest(){couti am a public method of a singleton classendl;}private:TaskQueue()default;staticTaskQueue*m_taskq;staticmutex m_mutex;};mutex TaskQueue::m_mutex;TaskQueue*TaskQueue::m_taskqnullptr;使用互斥锁对new操作创建实例时进行加锁操作防止同时创建多个实例但是程序执行的效率太低多线程访问单例对象时都是顺序访问2引入双重检查锁定双重检查锁定只有第一次访问时是顺序执行的在TaskQueue被实例化出来之后其他线程再去访问单例对象就是并行的了不会进入if内。//懒汉模式 引入双重检查锁定classTaskQueue{public:TaskQueue(constTaskQueuet)delete;TaskQueueoperator(constTaskQueuet)delete;staticTaskQueue*getInstance(){//双重检查锁定if(m_taskqnullptr){m_mutex.lock();if(m_taskqnullptr)m_taskqnewTaskQueue;m_mutex.unlock();}returnm_taskq;}voidprintTest(){couti am a public method of a singleton classendl;}private:TaskQueue()default;staticTaskQueue*m_taskq;staticmutex m_mutex;};mutex TaskQueue::m_mutex;TaskQueue*TaskQueue::m_taskqnullptr;3引入原子变量通过引入双重检查锁定的方式解决了在懒汉模式下多线程访问单例对象时出现的线程安全问题从表面上观察引入双重检查锁定的方式是十分完美的但是从底层上依旧存在漏洞对于m_taskq new TaskQueue;操作其对应的机器指令并不是一条而有三条对于计算机来说代码都是二进制指令/机器指令step1创建一块内存没有数据 step2创建 TaskQueue 类型的对象并将数据写入到对象中 step3为 m_taskq 对象指针初始化将有效的内存地址传递给 m_taskq 对象指针在实际的执行过程中m_taskq new TaskQueue;对应的机器指令可能会被重新排序成为step1创建一块内存没有数据 step3为 m_taskq 对象指针初始化将有效的内存地址传递给 m_taskq 对象指针 step2创建 TaskQueue 类型的对象并将数据写入到对象中如果线程A执行完成前两步之后失去CPU时间片被挂起此时线程B在进行指针判断时发现指针m_taskq不为空但该指针指向内存没有被初始化导致线程B使用了一个没有被初始化的队列对象就会出现问题出现问题是概率性的在C11中引入原子变量 atomic在底层控制机器指令的执行顺序可以实现一种更加安全的懒汉模式代码如下使用原子变量atomic的store()方法来存储单例对象使用load()方法来加载单例对象在原子变量中这两个函数在处理指令的时候默认的原子顺序是 memory_order_seq_cst 顺序原子操作使用顺序约束原子操作库整个函数的执行都将保证顺序执行并且不会出现数据竞态 data races缺点使用这种方法实现的懒汉模式的单例执行效率更低一些对代码进行以下修改通过原子变量将类的实例对象保存起来m_taskq 指针指向的内存类外初始化 指针指向为nullptr对 getInstance 方法进行相关的修改操作多线层在调用 getInstance 方法时 需要从原子变量中加载任务队列的实例抢到互斥锁的线程将继续向下执行 创建实例对象//懒汉模式 引入原子变量classTaskQueue{public:TaskQueue(constTaskQueuet)delete;TaskQueueoperator(constTaskQueuet)delete;staticTaskQueue*getInstance(){TaskQueue*taskqm_taskq.load();if(taskqnullptr){m_mutex.lock();taskqm_taskq.load();if(taskqnullptr){taskqnewTaskQueue;m_taskq.store(taskq);}m_mutex.unlock();}returnm_taskq.load();}voidprintTest(){couti am a public method of a singleton classendl;}private:TaskQueue()default;// static TaskQueue *m_taskq;staticatomicTaskQueue*m_taskq;staticmutex m_mutex;};mutex TaskQueue::m_mutex;atomicTaskQueue*TaskQueue::m_taskq;// TaskQueue* TaskQueue::m_taskq nullptr;4.懒汉线程安全-处理方式2在懒汉模式线程安全问题中除了可以通过引入双重检查锁定来解决线程安全问题还可以使用局部静态对象处理线程安全问题1设置局部静态对象使用静态的局部对象解决线程安全问题要求编译器必修支持C11标准在getInstance()局部函数中定义一个静态局部对象static TaskQueue taskq;调用无参构造初始化在C11标准中规定如果指令逻辑进入一个未被初始化的声明变量所有并发执行应当等待该变量完成初始化注使用静态的局部对象没有线程安全问题已经由C11标准中的编译器解决未被初始化的变量必须等待其完成初始化才能并发执行step1创建一块内存没有数据 step2创建 TaskQueue 类型的对象并将数据写入到对象中完成初始化操作 step3为 m_taskq 对象指针初始化将有效的内存地址传递给 m_taskq 对象指针// 懒汉模式 静态局部对象classTaskQueue{public:TaskQueue(constTaskQueuet)delete;TaskQueueoperator(constTaskQueuet)delete;staticTaskQueue*getInstance(){staticTaskQueue taskq;returntaskq;}voidprintTest(){couti am a public method of a singleton classendl;}private:TaskQueue()default;};5.案例运用1任务队列简单实现多线程拿到单例对象后在访问单例对象内部的数据时有线程安全问题多线程共享资源使用互斥锁保护多线程中共享的资源C11中给互斥锁加/解锁有两种方式方法1调用mutex对象的unlock();lock();方法方法2使用lock_gurd自动管理加/解锁lock_guardmutex locker(m_mutex);使用lock_gurd可以有效的避免死锁的问题自动加/解锁// 饿汉模式classTaskQueue{public:TaskQueue(constTaskQueuet)delete;TaskQueueoperator(constTaskQueuet)delete;staticTaskQueue*getInstance(){returnm_taskq;}voidprintTest(){couti am a public method of a singleton classendl;}// 判断任务队列是否为空boolisEmpty(){lock_guardmutexlocker(m_mutex);returnm_data.empty();}// 添加任务voidaddTask(intnode){lock_guardmutexlocker(m_mutex);m_data.push(node);}// 删除任务boolremoveTask(){lock_guardmutexlocker(m_mutex);if(m_data.empty())returnfalse;m_data.pop();returntrue;}// 获取队头任务inttakeTask(){lock_guardmutexlocker(m_mutex);if(m_data.empty())return-1;returnm_data.front();}private:TaskQueue()default;staticTaskQueue*m_taskq;// 任务队列queueintm_data;mutex m_mutex;};TaskQueue*TaskQueue::m_taskqnewTaskQueue;#includeiostream#includethread#includequeue#includemutexusingnamespacestd;intmain(){// 获取单例对象TaskQueue*taskqTaskQueue::getInstance();taskq-printTest();// 生产者线程// 使用匿名函数指定线程的处理动作threadt1([](){for(inti0;i25;i){taskq-addTask(i100);coutpush data:i100, threadId this_thread::get_id()endl;this_thread::sleep_for(chrono::milliseconds(500));//休眠500ms}});// 消费者线程threadt2([](){this_thread::sleep_for(chrono::milliseconds(100));while(!taskq-isEmpty()){// 开始消费cout--take data:taskq-takeTask(), threadId this_thread::get_id()endl;taskq-removeTask();this_thread::sleep_for(chrono::milliseconds(1000));//休眠500ms}});// 主线程阻塞 只有当t1、t2线程都结束后 主线程解除阻塞t1.join();t2.join();return0;}2用户登录当用户成功登录之后用户名和密码就会被存储到内存中可以创建一个单例类将用户数据保存到单例对象中classTest{public:staticTest*getInstance(){returnm_test;}// m_uservoidsetUserName(QString name){// 多线程下需要加锁解锁涉及写操作// lock();m_username;// unlock();}QStringgetUserName(){returnm_user;}// m_passwd// ....// ....// ....private:Test();Test(constTestt);staticTest*m_test;// static Test m_test;// 定义变量 - 属于唯一的单例对象QString m_user;QString m_passwd;QString m_ip;QString m_port;QString m_token;}Test*Test::m_testnewTest();// 初始化// Test Test::m_test;

相关新闻

对话量子场论(DQFT)深入研究报告:从协变作用量到场动力学、共识相变与可计算量化规则

对话量子场论(DQFT)深入研究报告:从协变作用量到场动力学、共识相变与可计算量化规则

对话量子场论(DQFT)深入研究报告:从协变作用量到场动力学、共识相变与可计算量化规则 作者:方见华 单位:世毫九实验室 体系归属:世毫九理论(SH9)认知统一场分支 前置基础&#xff1a…

2026/8/3 19:31:11 阅读更多 →
【N100小主机】体验不同系统

【N100小主机】体验不同系统

小主机的不同系统向日葵远程控制ubuntu一、简介二、问题及解决方法2.1 向日葵远程连接Ubuntu22主机黑屏?2.2 Ubuntu如何向日葵开机自启?2.3 无显示器情况下,windows远程桌面连接Ubuntu?三、美化桌面3.1 安装/解压3.2 设置3.3 右上角显示实时…

2026/8/3 19:31:10 阅读更多 →
计算机毕业设计之大学生体测管理系统

计算机毕业设计之大学生体测管理系统

随着信息技术和网络技术的飞速发展,人类已进入全新信息化时代,传统管理技术已无法高效,便捷地管理信息。为了迎合时代需求,优化管理效率,各种各样的管理系统应运而生,各行各业相继进入信息管理时代&#xf…

2026/8/3 19:31:10 阅读更多 →

最新新闻

Unity Mesh优化插件:从顶点合并到LOD生成的性能提升实战

Unity Mesh优化插件:从顶点合并到LOD生成的性能提升实战

1. 项目概述:为什么我们需要一个专门的Mesh优化插件? 在Unity游戏开发中,性能优化是一个永恒的话题,而Mesh(网格)作为构成3D模型的基础,其性能开销直接影响着游戏的帧率、内存占用和加载速度。一…

2026/8/3 20:06:25 阅读更多 →
RPA入门实战:从零打造数字员工,解放重复劳动

RPA入门实战:从零打造数字员工,解放重复劳动

1. 从“手工搬砖”到“数字员工”:我理解的RPA到底是什么最近几年,身边做财务、运营、客服的朋友,找我聊天的开场白经常变成:“我们公司最近在搞RPA,你们技术圈怎么看这个?” 或者更直接一点:“…

2026/8/3 20:06:24 阅读更多 →
Android内存深度诊断:smaps文件解析与实战应用

Android内存深度诊断:smaps文件解析与实战应用

1. 项目概述:为什么我们需要深入解析Android的smaps? 如果你在Android开发或者性能优化的路上摸爬滚打过一段时间,肯定遇到过这样的场景:应用在线上跑得好好的,突然收到用户反馈说“卡顿”、“闪退”,或者后…

2026/8/3 20:06:24 阅读更多 →
3个实战技巧:快速部署MaxKB企业级智能体平台

3个实战技巧:快速部署MaxKB企业级智能体平台

3个实战技巧:快速部署MaxKB企业级智能体平台 【免费下载链接】MaxKB 🔥 MaxKB is an open-source platform for building enterprise-grade agents. 强大易用的开源企业级智能体平台。 项目地址: https://gitcode.com/GitHub_Trending/ma/MaxKB 你…

2026/8/3 20:06:24 阅读更多 →
终极文档转换指南:用markitdown快速实现多格式转Markdown

终极文档转换指南:用markitdown快速实现多格式转Markdown

终极文档转换指南:用markitdown快速实现多格式转Markdown 【免费下载链接】markitdown Python tool for converting files and office documents to Markdown. 项目地址: https://gitcode.com/GitHub_Trending/ma/markitdown 在数字化办公时代,我…

2026/8/3 20:06:24 阅读更多 →
AutoJs自动化截图实战:从基础原理到高级应用场景

AutoJs自动化截图实战:从基础原理到高级应用场景

1. 项目缘起:为什么需要AutoJs自动化截图? 在移动端自动化测试、数据采集、日常任务批处理或者无障碍辅助功能开发中,我们经常会遇到一个看似简单却颇为繁琐的需求: 定时、定点或按条件对手机屏幕进行截图,并自动保存…

2026/8/3 20:05:24 阅读更多 →

日新闻

3个让你工作效率翻倍的Umi-OCR实战技巧:免费离线文字识别完全指南

3个让你工作效率翻倍的Umi-OCR实战技巧:免费离线文字识别完全指南

3个让你工作效率翻倍的Umi-OCR实战技巧:免费离线文字识别完全指南 【免费下载链接】Umi-OCR OCR software, free and offline. 开源、免费的离线OCR软件。支持截屏/批量导入图片,PDF文档识别,排除水印/页眉页脚,扫描/生成二维码。…

2026/8/3 0:00:47 阅读更多 →
[具身智能-181]:PC+服务器+具身机器人:构建具身智能从仿真到量产的闭环迭代混合架构

[具身智能-181]:PC+服务器+具身机器人:构建具身智能从仿真到量产的闭环迭代混合架构

PC服务器具身机器人:构建具身智能从仿真到量产的闭环迭代混合架构一、前言:具身智能需要“混合算力闭环系统”传统人工智能依赖云端静态数据集训练,不具备物理交互能力,无法适应真实世界的不确定性。具身智能(Embodied…

2026/8/3 0:00:47 阅读更多 →
[具身智能-181]:大分布式通信模型对比:看懂为什么 DDS 是 ROS2 底层通信最优解

[具身智能-181]:大分布式通信模型对比:看懂为什么 DDS 是 ROS2 底层通信最优解

前言构建机器人、具身智能这类分布式实时系统,通信底座直接决定整套系统的实时性、容错性、组网能力。分布式领域长期存在 4 类经典通信架构:点对点模式、Broker 中间代理模式、广播模式、以数据为中心(DDS)模式。很多开发者疑惑&…

2026/8/3 0:00:47 阅读更多 →

周新闻

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

1. 从水管网络到最大流:一个核心问题的诞生想象一下,你是一个城市供水系统的总工程师。你的城市有多个水源(水库),需要通过一个复杂的地下管道网络,将水输送到各个居民区。每条管道都有其最大通水能力&…

2026/8/3 4:58:13 阅读更多 →
基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

2026/8/3 1:53:31 阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/3 4:36:35 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/3 13:07:03 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/3 5:19:38 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/3 8:27:36 阅读更多 →