当前位置: 首页 > news >正文

yo!这里是单例模式相关介绍

目录

前言

特殊类设计

只能在堆上创建对象的类

1.方法一(构造函数下手)

 2.方法二(析构函数下手)

只能在栈上创建对象的类

单例模式

饿汉模式实现

懒汉模式实现

后记


前言

        在面向找工作学习c++的过程中,除了基本的语法知识以外,还有一些被反复使用、经验总结的设计模式或者说设计思想值得大家学习,也可以说是面试当中面试官大概率问到的面试题。在本节中,除了介绍标题所述的单例模式,还要了解一些特殊类的设计思想,当然也需要引入这些思想才能更好的理解单例模式。对于常见的特殊类的设计,比如不能被拷贝的类(将拷贝构造函数和赋值运算符重载设置为删除函数)、不能被继承的类(在类名后面加上final关键字)等这些都是一些较为简单的特殊类设计,难一点的像只能在堆或者栈上创建的类、只能创建一个对象的类(单例模式)这些又如何实现?往下看!

特殊类设计

  • 只能在堆上创建对象的类

1.方法一(构造函数下手)

        从构造函数下手,也就是把构造函数私有化,这样我们就可以控制谁能去使用构造函数了,控制的方法就是定义一个共有的接口,在该接口内使用允许的方法创建对象。这里就是创建了一个静态成员函数——getObj(),在接口内去new一个对象返回出去,为什么是静态呢?因为普通成员函数无法在类外使用,必须创建出对象才能使用,这就会产生“先有鸡还是先有蛋”的问题。

        此外,将拷贝构造函数和赋值运算符重载设置为删除函数,是为了防拷贝,因为拷贝与赋值可以在栈上或静态区创建对象,参考代码如下。

代码:

class HeapOnly
{
public:static HeapOnly* getObj(int a){return new HeapOnly(a);}private:HeapOnly(int a):_a(a){}HeapOnly(const HeapOnly& hp) = delete;HeapOnly& operator=(const HeapOnly& hp) = delete;private:int _a = 0;
};

调试:

 2.方法二(析构函数下手)

        除了将构造函数私有化,也可以将析构函数私有化,思想是一样的,即将析构函数私有化,之后定义一个共有的接口去使用允许的方式释放对象,如下代码块的delObj函数,那为什么不需要设置成静态函数呢?因为此时对象已经创建出来了,直接调用此函数来释放资源即可,其他方式创建出来的对象会因为没有析构函数而编译不通过。

        值得提一嘴的是,我们可以不用传入指向资源的指针到delObj函数内,因为普通成员函数有一个参数就是当前对象的指针,直接去释放即可。

代码:

class HeapOnly
{
public:void delObj(HeapOnly* pho){delete pho;}/*void delObj(){delete this;}*/
private:~HeapOnly(){}
private:int _a = 0;
};

调试:

  • 只能在栈上创建对象的类

        只能在栈上创建对象,这种就不能够在析构函数上下手了,考虑一下构造函数,思想其实与只能在堆上创建对象一样——设置共有的静态接口,在接口内定义对象返回出去,但其实这样的做法是不能做到严格把控的,也就是说写不出来只能在栈上创建对象的类,下面解释。

        我们先按之前的思想将其写出来,如下代码块。图一可以看到,的确只能在栈上创建对象,但是看图二,可以通过static将对象拷贝一份在静态区,这就是存在的bug。有人说将拷贝函数和赋值函数都设置为delete函数,但是这样就无法将getObj函数内定义的对象返回出来了,大家可以尝试一下,有解决的方法可以写在评论区,一块讨论。

代码:

class StackOnly
{
public:static StackOnly getObj(int a){return StackOnly(a);}private:StackOnly(int a = 0):_a(a){}public:int _a = 0;
};

单例模式

        一个类只能创建一个对象,即单例模式。单例模式是一种创建对象的设计模式,它确保一个类只有一个实例,并提供全局访问该实例的方式。

        在单例模式中,类的构造函数被私有化,使得外部无法直接创建对象。类内部维护一个静态变量来保存唯一的实例,通过一个公共的静态方法来获取该实例。当第一次调用该方法时,会创建一个对象并赋值给静态变量,以后的调用都直接返回这个静态变量。

        单例模式常用于需要限制全局资源访问的场景,比如数据库连接、线程池等。通过单例模式,可以确保全局只有一个实例存在,并提供一种方便的方式来访问该实例。有两个实现方式来实现单例模式:饿汉模式和懒汉模式

  • 饿汉模式实现

饿汉模式:不管你将来用不用,程序启动时就创建一个唯一的实例对象,也就是一开始(在main函数之前)就创建对象

优点:较简单,不存在线程安全问题

缺点:因为在main函数之前,所以可能会导致进程启动慢,而且如果有多个单例类对象实例启动顺序不确定

实现原理:

        首先,因为是单例模式,只能创建一个对象,所以先将拷贝构造函数、赋值运算符重载函数设置为删除函数,以防止拷贝。其次,按照前面特殊类设计思想,我们依旧将构造函数私有化,并定义一个共有函数用来创建符合条件的对象,这里需要只能创建一个对象,因此我们定义一个静态成员变量(可以是类指针,也可以是类对象),因为静态变量只有一份。静态成员变量是类内声明,类外初始化,因为是一开始就创建,所以在初始化时我们就new一个对象。然后,我们将定义一个共有的静态成员函数来获取这个变量即可,为什么这个成员函数也是静态的?因为普通成员函数内不能使用静态成员变量,代码参考如下:

代码:

class Singleton
{
public:static Singleton* getInstance(){return _inst;}
private:Singleton(){}Singleton(const Singleton& x) = delete;Singleton& operator=(const Singleton& x) = delete;static Singleton* _inst;
};
Singleton* Singleton::_inst = new Singleton();
  • 懒汉模式实现

        当单例对象构造十分耗时或者占用很多资源,比如加载插件、初始化网络连接、读取文件等,而且有可能该对象程序运行时不会用到,那么在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。

懒汉模式:在第一次使用时才创建单例对象

优点:进程启动无负载,多个单例实例启动顺序可以自由控制

缺点:复杂、存在线程安全问题

实现原理:

        如下代码块,懒汉模式实现过程与饿汉模式实现的大致框架都是一样的,主要在于因为懒汉模式是第一次使用时再创建这个对象,因此静态成员变量在初始化时设置为空。先看非线程安全版本的getInstance函数,如果静态成员变量_inst为空,说明还没创建,此时应该去创建这个对象,如果非空,说明已经创建过了,直接返回这个变量出去。

        其次,考虑到多个线程同时竞争创建这个实例,同时判断_inst为空,同时创建出了多个对象,导致线程安全问题,所以我们需要加锁保护,定义并初始化一把静态锁,在判断_inst是否为空之前加锁保护,这样就能保证进入判断的线程只有一个,其他线程都得挂起等待。

        但是发现在加锁的外面又加了一层判空的if语句,这是因为只需要第一次进来时需要用到锁,第二次往后进来都是只需要返回这个指针即可,否则每次进入这个函数都需要申请锁,会降低效率,这种方式叫做Double-Cheack,适用于第一次加锁后续不需要的情形。

        以上是懒汉模式的实现过程,下面再考虑一个问题——单例对象释放问题,首先一般情况下,单例对象是不需要释放的,因为一整个程序都可能需要用到它,单例对象在进程正常结束后也会自动释放;其次,有些场景需要释放,比如单例对象释放时,需要进行持久化操作(将数据信息保存在文件中),那么此时一个释放的方法就是可以通过内部类的方式实现单例对象释放机制。如下面代码块实现的DelInstance类,在成员中定义这样类型的静态对象,当程序结束时,系统会自动调用其析构函数从而释放单例对象,而所谓的持久化操作就可以在其析构函数内做。

代码:

class Singleton
{
public://非线程安全版本/*static Singleton* getInstance(){if (_inst == nullptr){_inst = new Singleton();}return _inst;}*/static Singleton* getInstance(){if(_inst==nullptr){lock_guard<mutex> lg(_mtx);if (_inst == nullptr){_inst = new Singleton();}}return _inst;}class DelInstance{public://持久化操作~DelInstance(){if (_inst)delete _inst;}};static DelInstance _delins;
private:Singleton(){}Singleton(const Singleton& x) = delete;Singleton& operator=(const Singleton& x) = delete;static Singleton* _inst;static mutex _mtx;
};
Singleton* Singleton::_inst = nullptr;
mutex Singleton::_mtx;   //注意:即使不需要初始化,静态成员变量也需要写在这

后记

        通过实现只能在堆或栈上创建对象的类,学习到如何实现单例模式,这是一种设计模式,是大多数程序员共同发现的“套路”,套用相同的模式可以让代码可读性更强、更容易被他人理解、使代码编写更加工程化。实现单例模式也是分为两种方法,其中更加建议使用懒汉模式。不仅仅是学会本文中特殊类设计的方法,更要注重设计的思想,对于面试中面对考官出的其他类的实现题目提供了一种思考或解决办法,好了,以上类的设计自己尝试实现一把,有问题举手。


相关文章:

yo!这里是单例模式相关介绍

目录 前言 特殊类设计 只能在堆上创建对象的类 1.方法一&#xff08;构造函数下手&#xff09; 2.方法二&#xff08;析构函数下手&#xff09; 只能在栈上创建对象的类 单例模式 饿汉模式实现 懒汉模式实现 后记 前言 在面向找工作学习c的过程中&#xff0c;除了基本…...

2023年上-未来几年我要做什么

1月份&#xff0c;离职。 2月份&#xff0c;春节休假回来&#xff0c;中旬去参加了一个月的瑜伽培训&#xff0c;学会了倒立、鹤蝉。。。。 3月份&#xff0c;瑜伽培训结束&#xff0c;开始收拾房子&#xff0c;并调研各类项目。 4月份&#xff0c;参与了朋友的区块链项目 …...

智能汽车竞赛摄像头处理(3)——动态阈值二值化(大津法)

前言 &#xff08;1&#xff09;在上一节中&#xff0c;我们学习了对图像的固定二值化处理&#xff0c;可以将原始图像处理成二值化的黑白图像&#xff0c;这里面的本质就是将原来的二维数组进行了处理&#xff0c;处理后的二维数组里的元素都是0和255两个值。 &#xff08;2…...

BGP协议

1.BGP相关概念 1.1 BGP的起源 不同自治系统&#xff08;路由域&#xff09;间路由交换与管理的需求推动了EGP的发展&#xff0c;但是EGP的算法简单&#xff0c;无法选路&#xff0c;从而被BGP取代。 自治系统&#xff1a;&#xff08;AS&#xff09; IGP&#xff1a;自治系统…...

一个完整工作流管理系统的组成部分

一个完整工作流管理系统的组成部分 一个完整的工作流管理系统通常由工作流引擎、工作流设计器、流程操作、工作流客户端程序、流程监控、表单设计器、与表单的集成以及与应用程序的集成八个部分组成。 一、工作流组成 1. 工作流引擎 工作流引擎作为工作流管理系统的核心部分&…...

鱼和熊掌如何兼得?一文解析RDS数据库存储架构升级

在2023年云栖大会上&#xff0c;阿里云数据库产品事业部负责人李飞飞在主题演讲中提到&#xff0c;瑶池数据库推出“DB存储”一体化能力&#xff0c;结合人工智能、机器学习、存储等方法和创新能力&#xff0c;实现Buffer Pool Extension能力和智能冷温热数据分层能力。在大会的…...

中科大计网学习记录笔记(五):协议层次和服务模型

前言&#xff1a; 学习视频&#xff1a;中科大郑烇、杨坚全套《计算机网络&#xff08;自顶向下方法 第7版&#xff0c;James F.Kurose&#xff0c;Keith W.Ross&#xff09;》课程 该视频是B站非常著名的计网学习视频&#xff0c;但相信很多朋友和我一样在听完前面的部分发现信…...

同构异机迁移方案2_目标服务器仅安装数据库软件scp物理文件

源端和目标端的数据库版本需要保持一致&#xff0c;补丁版本可以不一致&#xff0c;目标端磁盘空间不能小于源端空间&#xff0c;目标端只需要安装 Oracle 软件即可。 特别说明&#xff1a;本文档案例Oracle的安装路径不同&#xff0c;数据目录一致&#xff0c;采用scp的方式实…...

华为机考入门python3--(6)牛客6-质数因子

分类&#xff1a;质数、素数 知识点&#xff1a; 取余符号% 5%3 2 取整符号// 5//3 1 list中int元素转str map(str, list) 题目来自【牛客】 def prime_factors(n): """ 输入一个正整数n&#xff0c;输出它的所有质因子&#xff08;重复的也…...

11月最新版付费进群源码自动定位+开源

Nginx 1.22.1 php5.6 mysql5.6 数据库配置&#xff1a;/config/database.php 配置后台域名&#xff1a;config/extra/ip.php 设置伪静态thinkphp 后台账号88886666 密码12345 代码结构 关键代码剖析 <?php // ----------------------------------------------------…...

Python算法题集_旋转图像

Python算法题集_旋转图像 题目48&#xff1a;旋转图像1. 示例说明2. 题目解析- 题意分解- 优化思路- 测量工具 3. 代码展开1) 标准求解【矩阵复本】2) 改进版一【矩阵转置矩阵反转】3) 改进版二【四值旋转】 4. 最优算法 题目48&#xff1a;旋转图像 本文为Python算法题集之一…...

[ChatGPT们】ChatGPT 如何辅助编程初探

主页&#xff1a;元存储的博客 全文 9000 字&#xff0c; 原创请勿转载。 我没有写过诗&#xff0c;但有人说我的代码像诗一样优雅 -- 雷军 图片来源&#xff1a;https://www.bilibili.com/video/BV1zL411X7oS/ 1. 引言 作为一个程序员&#xff0c;我们不仅要熟悉各种编程语…...

深入Spring MVC的工作流程

深入Spring MVC的工作流程 在Spring MVC的面试问题中&#xff0c;常常被询问到的一个问题。Spring MVC的程序中&#xff0c;HTTP请求是如何从开始到结束被处理的。为了研究这个问题&#xff0c;我们将需要深入学习一下Spring MVC框架的核心过程和工作流程。 1. 启动请求生命周…...

我的数据结构c(给自己用的)

目录 顺序表&#xff1a; 链表&#xff1a; 栈&#xff1a; 队列&#xff1a; 我想在之后的大学数据结构课上需要自己写来做题&#xff0c;但每次都自己写&#xff0c;那太麻烦了&#xff0c;所以我就将这个博客来把所有的C语言的数据结构弄上去&#xff0c; 问我为什么不…...

使用Arcgis对欧洲雷达高分辨率降水数据重投影

当前需要使用欧洲高分辨雷达降水数据&#xff0c;但是这个数据的投影问题非常头疼。实际的投影应该长这样&#xff08;https://gist.github.com/kmuehlbauer/645e42a53b30752230c08c20a9c964f9?permalink_comment_id2954366https://gist.github.com/kmuehlbauer/645e42a53b307…...

[Python] scikit-learn中数据集模块介绍和使用案例

sklearn.datasets模块介绍 在scikit-learn中&#xff0c;可以使用sklearn.datasets模块中的函数来构建数据集。这个模块提供了用于加载和生成数据集的函数。 API Reference — scikit-learn 1.4.0 documentation 以下是一些常用的sklearn.datasets模块中的函数 load_iris() …...

Qt-互斥量-临界区-QMutex-QMutexLocker-QReadWriteLock

文章目录 1.QMutex2.QMutexLocker3.QReadWriteLock 在Qt中&#xff0c;互斥量&#xff08;Mutex&#xff09;是用于同步多线程访问共享资源的一种机制。临界区&#xff08;Critical Section&#xff09;是指一段必须由单个线程执行的代码区域&#xff0c;防止多个线程同时执行这…...

《PCI Express体系结构导读》随记 —— 第II篇 第4章 PCIe总线概述(6)

接前一篇文章&#xff1a;《PCI Express体系结构导读》随记 —— 第II篇 第4章 PCIe总线概述&#xff08;5&#xff09; 4.1 PCIe总线的基础知识 与PCI总线不同&#xff0c;PCIe总线使用端到端的连接方式&#xff0c;在一条PCIe链路的两端只能各连接一个设备&#xff0c;这两个…...

uniapp 高德地图显示

1. uniapp 高德地图显示 使用前需到**高德开放平台&#xff08;https://lbs.amap.com/&#xff09;**创建应用并申请Key   登录 高德开放平台&#xff0c;进入“控制台”&#xff0c;如果没有注册账号请先根据页面提示注册账号   打开 “应用管理” -> “我的应用”页面…...

2024年最新幻兽帕鲁服务器搭建教程

玩转幻兽帕鲁服务器&#xff0c;阿里云推出新手0基础一键部署幻兽帕鲁服务器教程&#xff0c;傻瓜式一键部署&#xff0c;3分钟即可成功创建一台Palworld专属服务器&#xff0c;成本仅需26元&#xff0c;阿里云服务器网aliyunfuwuqi.com分享2024年新版基于阿里云搭建幻兽帕鲁服…...

vscode里如何用git

打开vs终端执行如下&#xff1a; 1 初始化 Git 仓库&#xff08;如果尚未初始化&#xff09; git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...

【kafka】Golang实现分布式Masscan任务调度系统

要求&#xff1a; 输出两个程序&#xff0c;一个命令行程序&#xff08;命令行参数用flag&#xff09;和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽&#xff0c;然后将消息推送到kafka里面。 服务端程序&#xff1a; 从kafka消费者接收…...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)

宇树机器人多姿态起立控制强化学习框架论文解析 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架&#xff08;一&#xff09; 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

三体问题详解

从物理学角度&#xff0c;三体问题之所以不稳定&#xff0c;是因为三个天体在万有引力作用下相互作用&#xff0c;形成一个非线性耦合系统。我们可以从牛顿经典力学出发&#xff0c;列出具体的运动方程&#xff0c;并说明为何这个系统本质上是混沌的&#xff0c;无法得到一般解…...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

【JavaWeb】Docker项目部署

引言 之前学习了Linux操作系统的常见命令&#xff0c;在Linux上安装软件&#xff0c;以及如何在Linux上部署一个单体项目&#xff0c;大多数同学都会有相同的感受&#xff0c;那就是麻烦。 核心体现在三点&#xff1a; 命令太多了&#xff0c;记不住 软件安装包名字复杂&…...

Android第十三次面试总结(四大 组件基础)

Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成&#xff0c;用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机&#xff1a; ​onCreate()​​ ​调用时机​&#xff1a;Activity 首次创建时调用。​…...

LeetCode - 199. 二叉树的右视图

题目 199. 二叉树的右视图 - 力扣&#xff08;LeetCode&#xff09; 思路 右视图是指从树的右侧看&#xff0c;对于每一层&#xff0c;只能看到该层最右边的节点。实现思路是&#xff1a; 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...

Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)

引言 在人工智能飞速发展的今天&#xff0c;大语言模型&#xff08;Large Language Models, LLMs&#xff09;已成为技术领域的焦点。从智能写作到代码生成&#xff0c;LLM 的应用场景不断扩展&#xff0c;深刻改变了我们的工作和生活方式。然而&#xff0c;理解这些模型的内部…...