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;   //注意:即使不需要初始化,静态成员变量也需要写在这

后记

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


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/368949.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

查看自己电脑是arm还是x64(x86);linux操作系统识别

1、查看自己电脑是arm还是x64&#xff08;x86&#xff09; linux 参考&#xff1a; https://liuweiqing.blog.csdn.net/article/details/131783851 uname -a如果输出是 x86_64&#xff0c;那么你的系统是 64 位的 x86 架构&#xff08;通常我们称之为 x64&#xff09;。如果…

【annie/lux 快速下载哔哩哔哩视频】全网最简单,只需要5步!!!

1.首先 现在annie更名为lux 官网地址&#xff1a;https://github.com/iawia002/lux/releases 2.进入官网之后如图所示 3.下载lux软件 4.下载lux 这里需要说明一下 如果不下载这个的话也可以下载视频 但是视频和音频是分开的&#xff0c;你的视频没有声音 5.下载视频

Hive 主要内容一览

Hive架构 用户接口&#xff1a;Client CLI&#xff08;command-line interface&#xff09;、JDBC/ODBC(jdbc访问hive) 元数据&#xff1a;Metastore 元数据包括&#xff1a;表名、表所属的数据库&#xff08;默认是default&#xff09;、表的拥有者、列/分区字段、表的类型&am…

面试150 位1的个数 位运算

Problem: 191. 位1的个数 文章目录 思路复杂度Code 思路 &#x1f468;‍&#x1f3eb; 参考 复杂度 Code public class Solution {// you need to treat n as an unsigned valuepublic int hammingWeight(int n){int res 0;while (n ! 0){res 1;n & n - 1;// 把最后…

Python(SQLite)executescript用法

SQLite 数据库模块的游标对象还包含了一个 executescript() 方法&#xff0c;这不是一个标准的 API 方法&#xff0c;这意味着在其他数据库 API 模块中可能没有这个方法。但是这个方法却很实用&#xff0c;它可以执行一段 SQL 脚本。 例如&#xff0c;如下程序使用 executescr…

Spring Cloud + Vue前后端分离-第16章 项目功能升级

源代码在GitHub - 629y/course: Spring Cloud Vue前后端分离-在线课程 Spring Cloud Vue前后端分离-第16章 项目功能升级 BUG修复与功能优化 16-1 已提交的代码讲解 1.将gateway中的路由配置改为IP&#xff0c;用lb://时&#xff0c;有时候会有延时&#xff0c;需要等一会…

【Qt5小项目】接金币小游戏

代码量在250行左右&#xff0c; 需要源码的可以私信我。

ElementUI Data:Table 表格

ElementUI安装与使用指南 Table 表格 点击下载learnelementuispringboot项目源码 效果图 el-table.vue&#xff08;Table表格&#xff09;页面效果图 项目里el-table.vue代码 <script> export default {name: el_table,data() {return {tableData: [{dat…

[office] 在Excel2010中设定某些单元格数据不参与排序的方法介绍 #其他#知识分享#笔记

在Excel2010中设定某些单元格数据不参与排序的方法介绍 在Excel中排序&#xff0c;相信大家都会了&#xff0c;直接将一组数据按照从小到大或者从大到小进行排序&#xff0c;但是&#xff0c;现在要求我们规定其中几组数据不进行排序&#xff0c;只排序其余的部分。又该如何操作…

【数据分享】1929-2023年全球站点的逐月降雪深度数据(Shp\Excel\免费获取)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、能见度等指标&#xff0c;说到气象数据&#xff0c;最详细的气象数据是具体到气象监测站点的数据&#xff01; 之前我们分享过1929-2023年全球气象站点的逐月平均气温数据、逐月最高气温数据…

Linux 网络编程 + 笔记

协议&#xff1a;一组规则 分层模型结构&#xff1a; OSI七层模型&#xff1a;物理层、数据链路层、网络层、传输层、会话层、表示层、应用层TCP/IP 4层模型&#xff1a;链路层/网络接口层、网络层、传输层、应用层 应用层&#xff1a;http、ftp、nfs、ssh、telnet、传输层&am…

GPT3.5\GPT4系列计算完整prompt token数的官方方法

前言: ChatGPT如何计算token数&#xff1f;https://wtl4it.blog.csdn.net/article/details/135116493?spm1001.2014.3001.5502https://wtl4it.blog.csdn.net/article/details/135116493?spm1001.2014.3001.5502 GPT3.5\GPT4系列计算完整prompt token数的官方方法&#xff1…

Unity3d C# 在WebGL平台加载并解析xml文件实现总结

前言 xml是可扩展标记语言&#xff0c;由一系列的元素、属性、值节点等构成的一个树形结构&#xff0c;除了可读性差一点&#xff0c;别的用于存储一些结构化的数据还是比较方便的。这个功能在Unity3d端的实现是比较方便快捷的&#xff1a; void GetXML1() {string filePath …

K8S之Pod的介绍和使用

Pod的理论和实操 pod理论说明Pod介绍Pod运行与管理Pod管理多个容器Pod网络Pod存储 Pod工作方式自主式Pod控制器管理的Pod&#xff08;常用&#xff09; 创建pod的流程 pod实操通过资源清单文件创建自主式pod通过kubectl run创建Pod&#xff08;不常用&#xff09; pod理论说明 …

Unity根据落点和抛物线运行时间,求初始力

抛物线运行时长为2秒&#xff1a; 抛物线运行时长为4秒&#xff1a; 原理就是&#xff1a; 在竖直方向只受重力&#xff0c;做匀加速直线运动&#xff0c;水平不受力&#xff0c;做匀速直线运动。 代码&#xff1a; public void Update(){if (Input.GetKeyDown(KeyCode.Space)…

2024.1.30报错记录

今天想调通一套github上的U-net代码&#xff0c;把报错记录一下 第一章 ModuleNotFoundError: No module named skimage 没有这个模块 pip install scikit-image 第二章 TypeError: (‘Keyword argument not understood:‘, ‘input‘) TypeError: (‘Keyword argument …

数据图表方案,企业视频生产数据可视化

在信息爆炸的时代&#xff0c;如何将复杂的数据转化为直观、生动的视觉信息&#xff0c;是企业在数字化转型中面临的挑战。美摄科技凭借其独特的数据图表方案&#xff0c;为企业在数据可视化领域打开了一扇全新的大门。 一、数据图表方案的优势 1、高效便捷&#xff1a;利用数…

文件上传的另类应用

1.Imagemagick CVE-2016-3714 CVE-2022-44268 CVE-2020-29599可在vulhub靶场进行复现1.1.Imagemagick简介 ImageMagic是一款图片处理工具&#xff0c;当传入一个恶意图片时&#xff0c;就有可能存在命令注入漏洞。 ImageMagick默认支持一种图片格式mvg&#xff0c;而mvg与svg…

yii2 mongodb 操作

->where([<>,review,""]) 不等于空 $where [newstypeid>[$in>$categoryIdArr]]; 类似mysql in操作 &#xff08;$categoryIdArr是数组&#xff09; ->where([label>[$regex >赞美诗]]) 模糊搜索操作 $where [status>1,name>[$rege…

sentinel的Context创建流程分析

sentinel入门 功能 限流&#xff1a;通过限制请求速率、并发数或者用户数量来控制系统的流量&#xff0c;防止系统因为流量过大而崩溃或无响应的情况发生。 熔断&#xff1a;在系统出现故障或异常时将故障节点从系统中断开&#xff0c;从而保证系统的可用性。 降级&#xf…