探索设计模式——单例模式详解
前言:设计模式的作用主要是为了——利用设计方式的重用来自动地提高代码的重新利用、提高代码的灵活性、节省时间, 提高开发效率、低耦合,封装特性显著, 接口预留有利于扩展。
设计模式的种类有很多种,本篇内容主要讲解其中的单例模式。
什么是单例模式
单例模式是创建型模式的一种。顾名思义, 单例模式就是全局只有一个实例化对象。该种模式可以保证一个类不能创建新的对象。 也就是不能直接定义和new。 单例模式保证了全局只有唯一一份实例化对象, 而且它能够自行实例化并且向整个系统提供这个实例。这种类,提供了全局的方法, 叫做单例类, 而这种设计方法, 叫做单例模式。
同时, 单例模式又分为饿汉模式和懒汉模式两种。下面为他们两个的特性:
- 饿汉模式:程序开始前先将唯一实例创建出来。
- 懒汉模式:第一次使用时再将唯一实例创建出来。
两者的优缺点:
- 饿汉模式:优点是设计简单。但缺点是可能导致程序的启动较慢,不能延迟加载。并且当有多个单例对象的时候, 哪个对象先生成不确定。
- 懒汉模式:优点是解决了程序启动慢的问题, 延迟了加载。 并且当有多个单例对象的时候, 可以创建的顺序。 但缺点是设计比较复杂, 同时存在线程安全问题, 解决线程安全问题会导致程序的性能降低。
综上, 我们要知道单例模式的三个主要特性:
- 一个类里面只能有一份实例。
- 这个类能够自行创建这个实例。
- 必须向整个系统提供这个实例。
为什么要有单例模式:
在我们的电脑中其实有许多程序我们都只能打开一次, 其实这就是应用了单例模式。 比如当我们打开“设置”, 如果你多次点击“设置”。 他也只能打开一个设置窗口(如果你打开了多个,并且不是你本人自己修改了操作系统。那么请给你的电脑杀一下毒)。 我们不妨想一下, 如果我们能够打开多个设置, 那么是不是就显得有点多余, 因为我们只使用一个设置窗口就可以,不需要使用多个。开启多个设置窗口不仅会占用系统上的资源,并且, 如果我们能够打开多个设置窗口。 那么我们在这个窗口设置成这个样子, 在那个窗口设置成那个样子。 最终, 设置的结果是用哪个窗口的呢?所以, 这都是问题。 而单例模式就是为了解决这些的问题。

代码实现
饿汉模式
饿汉模式就是在程序启动之前就自行创建好了这个实例。 这一点我们可以通过静态成员变量来实现, 同时为了保证类的封装性, 我们将这个静态成员变量私有化:
class Only_Instance {private: static Only_Instance _inst; //私有化唯一实例};Only_Instance Only_Instance::_inst; //静态成员变量的定义。
然后为了保证全局只有唯一一份实例, 我们需要私有化该类的构造函数, 这样就能保证类外不能随意创建对象。
class Only_Instance {private: static Only_Instance _inst; //类唯一实例//私有化构造函数private: Only_Instance() {}; private: Only_Instance(const Only_Instance&) = delete; };Only_Instance Only_Instance::_inst; //静态成员变量定义
如此一来, 我们就能保证:该类只有唯一一份实例化对象。 并且能够在程序启动之前自行创建一份实例。
那么就要解决访问这个唯一一份实例的问题, 我们如何对它进行访问? 我们可以通过一个共有的方法来访问它, 并且因为我们没有实例化对象调用成员函数以及方法的全局性, 所以这个方法必须是静态的, 全局的:
class Only_Instance {private: static Only_Instance _inst; //类唯一实例//私有化构造函数private: Only_Instance() {}; private: Only_Instance(const Only_Instance&) = delete; public: static Only_Instance* getInstance() //公有的全局方法{return &_inst;}};Only_Instance Only_Instance::_inst; //静态成员变量定义
这样,我们就设计出了一个简单的饿汉模式。但是饿汉的不能延迟加载, 启动慢是一个问题。 为了解决, 这个问题。 我们这里看一下另一个设计模式——懒汉模式
懒汉模式
懒汉模式是在第一次使用的时候自行生成实例。 能够延迟加载, 解决了启动慢的问题。
懒汉模式同样需要私有化构造函数, 不能在外部创建实例化对象:
class Only_Instance {//构造函数private:Only_Instance() {};private:Only_Instance(const Only_Instance&) = delete;};
为了能够第一次才实例化对象, 我们需要在类地内部创建对象。 那么就不能定义一个全局的单例对象, 应该定义一个全局的单例对象指针, 并且这个指针也是私有的:
class Only_Instance {//构造函数private:Only_Instance() {};private:Only_Instance(const Only_Instance&) = delete;private:static Only_Instance* _inst; //全局的对象指针。};
那么如何才能访问这个指针, 我们用到和饿汉相同的方式。 就是定义一个全局的静态方法:
class Only_Instance {//构造函数private:Only_Instance() {};private:Only_Instance(const Only_Instance&) = delete;private:static Only_Instance* _inst; //全局的对象指针。public:static Only_Instance* getInstance() //访问inst{if (_inst == nullptr) {_inst = new Only_Instance();}return _inst;}};
这样, 就能创建一个通过受控地_inst, 随时访问我们规定的方法getInstance。
然后, 为了能够手动的销毁这个实例, 我们还要提供一个自动释放的接口:
//销毁实例化对象public static void DelInstance() {delete _inst;}
并且,为了资源能够自动释放。 我们可以定义一个内部类成员。 当这个成员销毁的时候, 那么调用inst的析构, 即:
class Only_Instance {//构造函数private:Only_Instance() {};private:Only_Instance(const Only_Instance&) = delete;private:static Only_Instance* _inst; //全局的对象指针。public:static Only_Instance* getInstance() //访问inst{if (_inst == nullptr){_inst = new Only_Instance();}return _inst;}//销毁实例化对象static void DelInstance() {delete _inst;}class auto_des{public:~auto_des() {delete _inst;}};static auto_des _ad;};Only_Instance::auto_des Only_Instance::_ad;
但是, 这个懒汉模式此时有另外的问题。 当涉及多线程时,如果我们一个程序跑到了if语段, 另一个程序也跑到了if语段。 那么如果两个if语段同时执行, 那么就会造成创建多个实例对象的情况。 为了避免这种情况,这里就要用到加锁地操作。 这个由于博主知识板块不太完整, 暂时不进行详细讲解。
想要了解地请看大佬的这篇文章:确保对象的唯一性——单例模式 (三)_青兮科技公司开发人员使用单例模式实现了负载均衡器的设计,但是在实际使用中出现-CSDN博客
其他简单特殊类设计
只能在堆上创建对象
只能在堆上创建对象, 只能在堆区创建对象。 那么我们就要让这个类无法随便创建对象。 那么使用的方法就是私有化或者封掉构造函数。 即:
class Only_Heap
{
public:Only_Heap* CreatObj(){return new Only_Heap;}//要注意将拷贝构造禁掉Only_Heap(const Only_Heap& pt) = delete;
private://Only_Heap(){cout << "构造对象" << endl;}int a = 1;
};
只能在栈上创建对象
只能在栈上创建对象和在堆上类似。 都是将构造函数封掉,然后在类里面创建对象返回给外面。
//只能在栈上创建对象class Only_Stack {public://公有一个在栈区创建对象的函数, 并进行值返回static Only_Stack* CreatObj(){Only_Stack obj;return &obj;}//对于new来说。 一般情况下调用自动生成的new。 但是我们也可以自己实现一个operator new。同时, 如果我们禁掉这个new。 那么编译器也不会自动生成了, 参数是size_t//void* operator new(size_t size) //{// cout << "operator new" << endl;// return malloc(size);//}/* Only_Stack(Only_Stack&) = delete;*/void* operator new (size_t size) = delete;private://首先私有构造函数。防止能够随便创建对象, 但是为了能在栈区创建对象, 还要定义构造Only_Stack() {}};
不能够继承的类
一个类如果不想被其他的类继承, 那么我们可以给这个类加上关键字final:
class _final_class final {//…………};
不能被拷贝的类
一个类如果不能进行拷贝工作, 那么就要将它的拷贝构造和拷贝赋值都封掉:
//设计一个类, 不能够进行拷贝class None_Copy {private://将拷贝构造和赋值重载只声明。 不实现。 禁止拷贝, 只需要让这个类无法调用拷贝构造和赋值重载即可。//1、为什么要设置成为私有, 是因为如果只声明, 不设置成私有, 那么如果用户在类外面定义了拷贝构造或者赋值重载, 那么就可以进行拷贝或者赋值操作了//2、为什么要只声明, 一方面是因为设置成私有并不会禁止类内部进行调用拷贝。 不定义就可以防止类的内部进行拷贝。 另一方面是因为定义了也不会用, 不写更简单None_Copy(const None_Copy& cp);None_Copy& operator=(const None_Copy& cp); };
以上, 就是本节的全部内容。 有关设计模式的知识, 博主现在学习的不多, 以后学习更多知识后会更新。 当下如果想要学习更多设计模式可以看一下这个大佬(下面放链接了)的文章, 他为自己的文章建立了一个索引:
史上最全设计模式导学目录(完整版)_史上最全设计模式lovelion-CSDN博客
LoveLion-CSDN博客
相关文章:
探索设计模式——单例模式详解
前言:设计模式的作用主要是为了——利用设计方式的重用来自动地提高代码的重新利用、提高代码的灵活性、节省时间, 提高开发效率、低耦合,封装特性显著, 接口预留有利于扩展。 设计模式的种类有很多种,本篇内容主要讲解…...
建筑垃圾/城市固废倾倒转移乱象:EasyCVR+AI智能视频监控方案助力城市环保监管
近日有新闻记者报道,中央生态环境保护督察组在上海、浙江、江西、湖北、湖南、重庆、云南7省市督察发现,一些地方建筑垃圾处置工作存在明显短板,乱堆乱倒问题时有发生,比如,江西湘东区在杨家田地块违规设置弃土场&…...
C的I/O操作
目录 引言 一、文件与目录操作 1. 打开与关闭文件 2. 文件读写操作 3. 文件定位与错误处理 二、字符流与字节流 1. 字符流处理 2. 字节流处理 三、序列化与反序列化 1. 序列化 2. 反序列化 四、新的I/O(NIO) 表格总结 文件与目录操作 字符…...
Android Audio实战——声道信息回调(五)
在前面的 AudioTrack 构造中,我们传入了音频的声道信息,这一节我们就来详细介绍一下声道的配置信息。 一、声道介绍 音频中的声道配置从单声道到双声道(立体声)、再到多声道系统(如5.1和7.1),代表了声音录制和回放技术的发展,旨在提供越来越丰富和沉浸式的听觉体验。 …...
ThreeJS给模型添加介绍文字(贴在模型上 不会一直面向我们)
使用到 FontLoader跟 TextGeometry 引包 import {TextGeometry} from "three/examples/jsm/geometries/TextGeometry"; import {FontLoader} from "three/examples/jsm/loaders/FontLoader";使用 // 创建字体加载器并加载字体 const fontLoader new Fo…...
[Qt] Qt Creator 以及 Qt 在线安装教程
一、Qt Creator 下载及安装 1、从以下镜像源下载安装包常规安装即可 Qt Creator 也可以在第二步Qt 在线安装时一次性勾选安装,见后文 Qt Creator 中科大源下载地址 二、Qt 在线安装 1、根据所在平台选择对应的安装器下载 Qt 在线安装器下载 2、可能的安装报错…...
【大分享05】动态容差归档,打通不动产登记管理“最后一公里”
关注我们 - 数字罗塞塔计划 - 本篇是参加由电子文件管理推进联盟联合数字罗塞塔计划发起的“大分享”活动投稿文章,来自上海涵妍档案信息技术有限责任公司,作者:陈雪。 一、政策背景 在“互联网政务服务”的浪潮下,各级政府机构…...
嵌入式模拟电路面试题大全及参考答案(持续更新)
目录 理想运算放大器的两个基本特性 共模抑制比(CMRR)及其重要性 负反馈在放大器中的作用 差分放大电路的工作原理 使用运算放大器构建非反相放大器 电源抑制比(PSRR) 带宽(BW)在放大器中的含义 计算RC低通滤波器的截止频率 基本的积分电路及其时间常数 增益-带…...
【C语言】解决C语言报错:Uninitialized Variable
文章目录 简介什么是Uninitialized VariableUninitialized Variable的常见原因如何检测和调试Uninitialized Variable解决Uninitialized Variable的最佳实践详细实例解析示例1:局部变量未初始化示例2:数组未初始化示例3:指针未初始化示例4&am…...
RabbitMQ实践——交换器(Exchange)绑定交换器
在《RabbitMQ实践——交换器(Exchange)和绑定(Banding)》一文中,我们实验了各种交换器。我们可以把交换器看成消息发布的入口,而消息路由规则则是由“绑定关系”(Banding)来定义&…...
使用 Vue 官方脚手架初始化 Vue3 项目
Vite 官网:https://cn.vitejs.dev/ Vue 官网:https://vuejs.org/ Vue 官方文档:https://cn.vuejs.org/guide/introduction.html Element Plus 官网:https://element-plus.org/ Tailwind CSS 官网:https://tailwindcss.…...
C语言中的宏定义(#define)和函数调用的区别
C语言中的宏定义(#define)和函数调用在概念、工作方式以及它们对代码的影响上有显著的区别。以下是它们之间的主要差异: 宏定义(#define) 工作方式:宏定义是在预处理阶段进行的文本替换。预处理器会在编译…...
196. 删除重复的电子邮箱
196. 删除重复的电子邮箱 题目链接:196. 删除重复的电子邮箱 代码如下: # Write your MySQL query statement below delete from Person as p where p.id not in(select e.id from (select min(id) as idfrom Person group by email ) as e )...
Android 大话binder通信 (上)
戳蓝字“牛晓伟”关注我哦! 用心坚持输出易读、有趣、有深度、高质量、体系化的技术文章 本文摘要 用故事的方式把binder通信的整个过程都描述出来,binder通信都经历了哪些节点,在这些节点上的数据有哪些变化,同时还对binder通…...
DevOps学习回顾01-技能发展路线-岗位能力-体系认知
事为先,人为重–事在人为 参考来源: 极客时间专栏:DevOps实战笔记,作者:石雪峰 课程链接:https://time.geekbang.org/column/intro/235 时代的典型特征 VUCA VUCA 是指易变性(Volatility&…...
【MySQL】复合查询和内外连接
文章目录 MySQL复合查询和内外连接1. 复合查询1.1 多表查询1.2 自连接1.3 子查询单行子查询多行子查询多列子查询from中使用子查询合并查询 2. 内外连接1. INNER JOIN2. LEFT JOIN3. RIGHT JOIN4. FULL JOIN5. CROSS JOIN MySQL复合查询和内外连接 1. 复合查询 1.1 多表查询 …...
【星海随笔】云解决方案学习日志篇(二) kafka、Zookeeper、Fielbeat
Elastic 中国社区官方博客 https://blog.csdn.net/ubuntutouch/category_9209092.html Kafka kafka的源代码是基于Scala语言编写的,运行在Java虚拟机(即:JVM)上。因此,在安装kafka之前需要先安装JDK Kafka 为什么依赖 Zookeepe…...
【测试专题】系统测试报告(原件Word)
软件测试报告在软件开发过程中起着至关重要的作用,主要有以下几个主要原因: 1、确保软件质量 2、提供决策支持 3、记录测试过程和结果 4、促进沟通和协作 5、符合标准和法规要求 6、改进测试流程和策略 7、降低风险 软件开发全套资料获取进主页或者本文末…...
C++中的模板方法模式
目录 模板方法模式(Template Method Pattern) 实际应用 数据处理流程 在线教育系统的课程模板 软件开发生命周期 总结 模板方法模式(Template Method Pattern) 模板方法模式是一种行为设计模式,它定义了一个操作…...
【数据结构】第十七弹---C语言实现选择排序
✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】 目录 1、选择排序 1.1、基本思想 1.2、代码实现 1.3、代码测试 1.4、时空复杂度分析 总结 1、选择排序 1.1、基本思想 选择排序是一种简单直观的比…...
多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能
1. 开发环境准备 安装DevEco Studio 3.1: 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK 项目配置: // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...
密码学基础——SM4算法
博客主页:christine-rr-CSDN博客 专栏主页:密码学 📌 【今日更新】📌 对称密码算法——SM4 目录 一、国密SM系列算法概述 二、SM4算法 2.1算法背景 2.2算法特点 2.3 基本部件 2.3.1 S盒 2.3.2 非线性变换 编辑…...
聚六亚甲基单胍盐酸盐市场深度解析:现状、挑战与机遇
根据 QYResearch 发布的市场报告显示,全球市场规模预计在 2031 年达到 9848 万美元,2025 - 2031 年期间年复合增长率(CAGR)为 3.7%。在竞争格局上,市场集中度较高,2024 年全球前十强厂商占据约 74.0% 的市场…...
