单例模式(饿汉模式 懒汉模式)与一些特殊类设计
文章目录
一、不能被拷贝的类
二、只能在堆上创建类对象
三、只能在栈上创建类对象
四、不能被继承的类
五、单例模式
5、1 什么是单例模式
5、2 什么是设计模式
5、3 单例模式的实现
5、3、1 饿汉模式
5、3、1 懒汉模式
🙋♂️ 作者:@Ggggggtm 🙋♂️
👀 专栏:C++ 👀
💥 标题:特殊类的设计💥
❣️ 寄语:与其忙着诉苦,不如低头赶路,奋路前行,终将遇到一番好风景 ❣️
一、不能被拷贝的类
一个类拷贝都是由拷贝构造来完成的。拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。代码如下:
class NonCopyableClass { public:NonCopyableClass() {}private:// 禁用拷贝构造函数NonCopyableClass(const NonCopyableClass&) = delete;// 禁用拷贝赋值运算符NonCopyableClass& operator=(const NonCopyableClass&) = delete; };int main() {NonCopyableClass obj1;// 编译错误,不能拷贝对象// NonCopyableClass obj2(obj1);//NonCopyableClass obj3 = obj1;return 0;
在C++98中,将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有也是可以的。
二、只能在堆上创建类对象
我们在栈和静态区创建对象时,都需要调用构造函数来初始化。同时,为了防止拷贝和赋值在栈空间上,所以将默认构造、拷贝构造、赋值重载都禁用了。
那我们可以在类内部单独提供一个动态申请对象的静态成员函数。具体代码如下:
class HeapOnly { public:// 提供一个公有的,获取对象的方式,对象控制是new出来的static HeapOnly* CreateObj(){return new HeapOnly;}// 防拷贝HeapOnly(const HeapOnly& hp) = delete;HeapOnly& operator=(const HeapOnly& hp) = delete;void Destroy() {delete this;} private:// 构造函数私有HeapOnly():_a(0){} private:int _a; };int main() {HeapOnly* hp = HeapOnly::CreateObj();//HeapOnly copy(*hp);hp->Destroy();return 0; }
三、只能在栈上创建类对象
为了防止在堆上或者静态区申请对象,构造函数应该私有。可以在类内部提供一个返回栈对象的方法。代码如下:
class StackOnly { public:static StackOnly CreateObj(){StackOnly st;return st;}private:// 构造函数私有StackOnly():_a(0){} private:int _a; };
但是,此时还能拷贝构造和赋值呢!!!那么能够禁用掉拷贝构造吗?答案是不能的。原因是我们通过传值返回的栈对象,此时必须需要拷贝。不能够传引用返回,因为是局部变量。出了函数就会被销毁。我们最多的就是禁用掉new的使用,具体代码如下:
class StackOnly { public:static StackOnly CreateObj(){StackOnly st;return st;}// 不能防拷贝//StackOnly(const StackOnly& st) = delete;StackOnly& operator=(const StackOnly& st) = delete;void* operator new(size_t n) = delete; private:// 构造函数私有StackOnly():_a(0){} private:int _a; };int main() {StackOnly st1 = StackOnly::CreateObj();// 拷贝构造static StackOnly copy2(st1);//StackOnly* copy3 = new StackOnly(st1);return 0; }
四、不能被继承的类
一个类不能被继承有两种方法:
- C++98方式:构造函数私有化,派生类中调不到基类的构造函数。则无法继承。
class NonInherit { public:static NonInherit GetInstance(){return NonInherit();} private:NonInherit(){} };
- C++11方法:final关键字,final修饰类,表示该类不能被继承。
class A final {// .... };
五、单例模式
5、1 什么是单例模式
单例模式是一种设计模式,用于确保在整个应用程序中只存在一个特定类的实例对象,该实例对象被所有程序模块共享。其主要目的是限制类的实例化操作,以确保在任何情况下都只能获得同一个实例。
比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。
5、2 什么是设计模式
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。为什么会产生设计模式这样的东西呢?就像人类历史发展会产生兵法。最开始部落之间打仗时都是人拼人的对砍。后来春秋战国时期,七国之间经常打仗,就发现打仗也是有套路的,后来孙子就总结出了《孙子兵法》。孙子兵法也是类似。使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。
5、3 单例模式的实现
单例模式实现的方法有两种:饿汉模式、懒汉模式。我们先来看一下饿汉模式的实现。
5、3、1 饿汉模式
所谓饿汉模式,就是在main函数之前,就会创建出对象。并不会考虑到你是否要用这个对象。那怎么实现呢?
首先,为了保证只能实例出一份对象,就必须把构造函数私有化。其次,需要在自己的类内部定义一个静态的类对象或者类指针。然后在类内部定义一些静态成员函数来初始化、获取对象/指针等操作。具体代码如下:
//class MemoryPool //{ //public: // //private: // // 构造函数私有化 // MemoryPool() // {} // // char* _ptr = nullptr; // // ... // // static MemoryPool _inst; // 声明 //}; //定义 //MemoryPool MemoryPool::_inst;class MemoryPool { public:static MemoryPool* GetInstance(){return _pinst;}void* Alloc(size_t n){void* ptr = nullptr;// ....return ptr;}void Dealloc(void* ptr){// ...}MemoryPool(MemoryPool& my) = delete;MemoryPool& operator= (MemoryPool& my) = delete; private:// 构造函数私有化MemoryPool(){}char* _ptr = nullptr;// ...static MemoryPool* _pinst; // 声明 };// 定义 MemoryPool* MemoryPool::_pinst = new MemoryPool;int main() {//MemoryPool pool1;//MemoryPool pool2;void* ptr1 = MemoryPool::GetInstance()->Alloc(10);MemoryPool::GetInstance()->Dealloc(ptr1); }
我们来分析一下:当类内定义的是静态成员时,需要在类外进行初始化。那么在类外能够调用私有的构造函数进行初始化吗?答案是可以的!因为该成员是属于类内部的私有成员,只不过是在类外进行的初始化。
那么能在类内定义非静态类对象吗?答案是不可以的,这本身就是语法错误。其次静态成员变量并不属于某个对象,而是属于整个类。
总结饿汉模式(Eager Initialization) 的优点:
- 实现简单,线程安全。在类加载时就创建了实例,没有线程安全问题。
- 可以保证在任何时候获取到同一个实例。
缺点:
- 类加载时即创建实例,尤其是在实例初始化比较耗时的情况下,会影响到程序的启动速度。
- 如果该实例一直没有被使用,则会造成内存的浪费。
- 当有多个单例对象时,饿汉模式无法很好的控制其初始化先后顺序。当然,在一个文件內部还好,在多个文件中就无法确定静态成员的初始化顺序。
5、3、1 懒汉模式
如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式(延迟加载)更好。
懒汉模式在第一次使用时才会创建单例对象,延迟实例化以节省资源。代码如下:class MemoryPool { public:static MemoryPool* GetInstance(){if (_pinst == nullptr){_pinst = new MemoryPool;}return _pinst;}void* Alloc(size_t n){void* ptr = nullptr;// ....return ptr;}void Dealloc(void* ptr){// ...}// 实现一个内嵌垃圾回收类 class CGarbo {public:~CGarbo(){if (_pinst)delete _pinst;}};MemoryPool(MemoryPool& my) = delete;MemoryPool& operator= (MemoryPool& my) = delete; private:// 构造函数私有化MemoryPool(){// ....}char* _ptr = nullptr;// ...static MemoryPool* _pinst; // 声明 };// 定义 MemoryPool* MemoryPool::_pinst = nullptr;// 回收对象,main函数结束后,他会调用析构函数,就会释放单例对象 static MemoryPool::CGarbo gc;
总结懒汉模式(Lazy Initialization) 优点:
- 延迟实例化,只有第一次调用获取实例的方法时才会创建对象,避免了资源浪费。
- 可以控制对象的初始化顺序。
- 不影响程序的启动速度。
缺点:
- 不是线程安全的,如果多个线程并发地调用获取实例的方法,可能会创建多个实例。
- 在多线程环境下,需要额外的同步措施来保证线程安全,增加了复杂性开销。
相关文章:

单例模式(饿汉模式 懒汉模式)与一些特殊类设计
文章目录 一、不能被拷贝的类 二、只能在堆上创建类对象 三、只能在栈上创建类对象 四、不能被继承的类 五、单例模式 5、1 什么是单例模式 5、2 什么是设计模式 5、3 单例模式的实现 5、3、1 饿汉模式 5、3、1 懒汉模式 🙋♂️ 作者:Ggggggtm &#x…...

133. 克隆图
133. 克隆图 题目-中等难度示例1. bfs 题目-中等难度 给你无向 连通 图中一个节点的引用,请你返回该图的 深拷贝(克隆)。 图中的每个节点都包含它的值 val(int) 和其邻居的列表(list[Node])。…...
交流耐压试验目的
试验目的 交流耐压试验是鉴定电力设备绝缘强度最有效和最直接的方法。 电力设备在运行中, 绝缘长期受着电场、 温度和机械振动的作用会逐渐发生劣化, 其中包括整体劣化和部分劣化,形成缺陷, 例如由于局部地方电场比较集中或者局部…...

使用 YCSB 和 PE 进行 HBase 性能压力测试
HBase主要性能压力测试有两个,一个是 HBase 自带的 PE,另一个是 YCSB,先简单说一个两者的区别。PE 是 HBase 自带的工具,开箱即用,使用起来非常简单,但是 PE 只能按单个线程统计压测结果,不能汇…...
正则表达式相关概念及不可见高度页面的获取
12.正则 概念:匹配有规律的字符串,匹配上则正确 1.正则的创建方式 构造函数创建 // 修饰符 igm// i 忽视 ignore// g global 全球 全局// m 换行 var regnew RegExp("匹配的内容","修饰符")var str "this is a Box";var reg new RegExp(&qu…...

深入学习 Redis - 分布式锁底层实现原理,以及实际应用
目录 一、Redis 分布式锁 1.1、什么是分布式锁 1.2、分布式锁的基础实现 1.2.1、引入场景 1.2.2、基础实现思想 1.2.3、引入 setnx 1.3、引入过期时间 1.4、引入校验 id 1.5、引入 lua 脚本 1.5.1、引入 lua 脚本的原因 1.5.2、lua 脚本介绍 1.6、过期时间续约问题&…...

Hive行转列[一行拆分成多行/一列拆分成多列]
场景: hive有张表armmttxn_tmp,其中有一个字段lot_number,该字段以逗号分隔开多个值,每个值又以冒号来分割料号和数量,如:A3220089:-40,A3220090:-40,A3220091:-40,A3220083:-40,A3220087:-40,A3220086:-4…...
TypeScript系列之类型 string
文章の目录 背景写在最后 背景 与JavaScript不同的是,TypeScript使用的是静态类型,比如说它指定了变量可以保存的数据类型。如下面代码所示,如果在JavaScript中,指定变量可以保存的数据类型,会报错:类型注…...

【C++】动态内存管理 ③ ( C++ 对象的动态创建和释放 | new 运算符 为类对象 分配内存 | delete 运算符 释放对象内存 )
文章目录 一、C 对象的动态创建和释放1、C 语言 对象的动态创建和释放 的方式2、C 语言 对象的动态创建和释放 的方式 二、代码示例 - 对象的动态创建和释放 一、C 对象的动态创建和释放 使用 C 语言中的 malloc 函数 可以为 类对象 分配内存 ; 使用 free 函数可以释放上述分配…...

AMS爆炸来袭,上线即巅峰
1.关于首发项目Antmons(AMS)空投结果 Gate.io Startup 首发项目Antmons代币AMS于Aug15th,AM 07:00开始下单,24小时内下单同等对待总共有15,950人下单,下单总价值超过1,000万美金分发系数约为0.001640495298341。根据上线规则AMS项目认购成功,…...

是面试官放水,还是公司实在是太缺人?这都没挂,华为原来这么容易进...
华为是大企业,是不是很难进去啊?” “在华为做软件测试,能得到很好的发展吗? 一进去就有9.5K,其实也没有想的那么难” 直到现在,心情都还是无比激动! 本人211非科班,之前在字节和腾…...

怒刷LeetCode的第2天(Java版)
目录 第一题 题目来源 题目内容 解决方法 方法一:滑动窗口 方法二:双指针加哈希表 第二题 题目来源 题目内容 解决方法 方法一:二分查找 方法二:归并排序 方法三:分治法 第三题 题目来源 题目内容 解…...
AUTOSAR汽车电子嵌入式编程精讲300篇-车载CAN总线网络的异常检测(续)
目录 车载 CAN 总线网络异常检测技术 3.1 车载 CAN 总线网络异常检测技术概述 3.1.1基于统计的异...

mojo安装
docker安装mojo 官网 https://developer.modular.com/login 很奇怪登录页面不显示 类似于网站劫持 docker 安装mojo带jupyterlab的方式 https://hub.docker.com/r/lmq886/mojojupyterlab 拉取镜像 docker pull lmq886/mojojupyterlab docker pull lmq886/mojojupyterlab:1.2 启…...

【探索Linux】—— 强大的命令行工具 P.8(进程地址空间)
阅读导航 前言一、内存空间分布二、什么是进程地址空间1. 概念2. 进程地址空间的组成 三、进程地址空间的设计原理1. 基本原理2. 虚拟地址空间 概念 大小和范围 作用 虚拟地址空间的优点 3. 页表 四、为什么要有地址空间五、总结温馨提示 前言 前面我们讲了C语言的基础知识&am…...

vue3 - Element Plus 切换主题色及el-button hover颜色不生效的解决方法
GitHub Demo 地址 在线预览 Element Plus 自定义主题官方文档 如果您想要通过 js 控制 css 变量,可以这样做: // document.documentElement 是全局变量时 const el document.documentElement // const el document.getElementById(xxx)// 获取 css 变…...

【C++面向对象侯捷】1.C++编程简介
文章目录 视频来源:我的百度网盘...

年龄大了转嵌入式有机会吗?
年龄大了转嵌入式有机会吗? 首先,说下结论:年龄并不是限制转行嵌入式软件开发的因素,只要具备一定的编程和电子基础知识,认真学习和实践,是可以成为优秀的嵌入式软件开发工程师的。最近很多小伙伴找我&…...
Mysql高级——索引优化和查询优化(2)
5. 排序优化 5.1 排序优化 问题:在 WHERE 条件字段上加索引,但是为什么在 ORDER BY 字段上还要加索引呢? 优化建议: SQL 中,可以在 WHERE 子句和 ORDER BY 子句中使用索引,目的是在 WHERE 子句中避免全表…...

SpringMVC的拦截器和JSR303的使用
目录 一、JSR303 二、拦截器(interceptor) 一、JSR303 1.1.什么是JSR303 JSR 303,它是Java EE(现在称为Jakarta EE)规范中的一部分。JSR 303定义了一种用于验证Java对象的标准规范,也称为Bean验证。 Bean验…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
大模型多显卡多服务器并行计算方法与实践指南
一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...

html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...

【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...