探索设计模式——单例模式详解

        前言:设计模式的作用主要是为了——利用设计方式的重用来自动地提高代码的重新利用、提高代码的灵活性、节省时间, 提高开发效率、低耦合,封装特性显著, 接口预留有利于扩展。

        设计模式的种类有很多种,本篇内容主要讲解其中的单例模式。

什么是单例模式

        单例模式是创建型模式的一种。顾名思义, 单例模式就是全局只有一个实例化对象。该种模式可以保证一个类不能创建新的对象。 也就是不能直接定义和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博客

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

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

相关文章

目标检测顶会新成果!20个突破性方法,更高性能,更强理解与分析能力!

【目标检测】在近年来的深度学习领域中备受关注&#xff0c;它通过识别和定位图像中的目标对象&#xff0c;提升了模型在图像理解和分析方面的能力。目标检测技术在自动驾驶、安防监控和医疗影像分析等任务中取得了显著成果。其独特的方法和卓越的表现使其成为研究热点之一。 为…

我整理的面向大模型的高质量语料集!

自己一直在做企业内部的垂类大模型&#xff0c;但随着运营深入&#xff0c;发现光有企业内部的领域数据还不足以微调出一个健壮的领域大模型&#xff0c;因为泛化能力比较弱。 在领域数据中融入公共数据进行训练是公认的提升领域大模型泛化能力的方法&#xff0c;下面是我整理的…

《 穿越时空的代码、在回首:Evil.js两年后的全新解读 》

破坏计算机系统罪可能香翅捞饭&#xff01;&#xff01;&#xff01; 本文以源码解析&#xff0c;场景复现&#xff0c;毒与药1.0.0攻防战&#xff0c;来主导本次攻击下毒、防守破解 只有周日才注入&#xff0c;当周日产生bug时&#xff0c;工作日程序员进行debug时将不会进行…

JavaScript和promise——0_1 promise

文章目录 是什么&#xff1f;未来值回调和未来值在回调环境下这么和未来值交互&#xff1f;群居的未来值其他的解决方案 这样写可以实现目标效果。可是&#xff0c;这样写优雅吗&#xff1f; 英雄登场关键词&#xff1a;then关键词&#xff1a;回调 为什么promise不需要start函…

【机器学习】CART决策树算法的核心思想及其大数据时代银行贷款参考案例——机器认知外界的重要算法

目录 引言 概述 CART决策树的特点 核心思想 减少不确定性的指标 基尼系数&#xff08;Gini Index&#xff09; 分类错误率 熵 银行实例 背景 数据准备 模型构建 模型评估与优化 应用与结果 代码示例 ✈✈✈✈引言✈✈✈✈ CART算法既可以用于分类问题&#xff0…

Simulink代码生成: 状态机的其他建模方法

本文研究状态机建模的一些方法和技巧。 文章目录 1 引入2 状态机建模方法2.1 状态机中的计时2.2 状态机中的计数2.3 转移顺序 3 总结 1 引入 博主一直很喜欢用Simulink中的状态机建模&#xff0c;在这里想记录一下自己平时使用Stateflow建模的心得。因为自身行业所限&#xff…

深入理解并打败C语言难关之一————指针(3)

前言&#xff1a; 昨天把指针最为基础的内容讲完了&#xff0c;并且详细说明了传值调用和传址调用的区别&#xff08;这次我也是做到了每日一更&#xff0c;感觉有好多想写的但是没有写完&#xff09;&#xff0c;下面不多废话&#xff0c;下面进入本文想要说的内容 目录&#…

【数据结构】第十七弹---C语言实现选择排序

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1、选择排序 1.1、基本思想 1.2、代码实现 1.3、代码测试 1.4、时空复杂度分析 总结 1、选择排序 1.1、基本思想 选择排序是一种简单直观的比…

【测试专题】系统测试报告(原件Word)

软件测试报告在软件开发过程中起着至关重要的作用&#xff0c;主要有以下几个主要原因&#xff1a; 1、确保软件质量 2、提供决策支持 3、记录测试过程和结果 4、促进沟通和协作 5、符合标准和法规要求 6、改进测试流程和策略 7、降低风险 软件开发全套资料获取进主页或者本文末…

如何判断三相交流电子负载的性能

三相交流电子负载是模拟实际负载的设备&#xff0c;用于测试电源、变频器、逆变器等电力电子设备的性能。在购买和使用三相交流电子负载时。 三相交流电子负载能够稳定输出的最大有功功率&#xff0c;额定功率越高&#xff0c;说明负载的承载能力越强。在选择三相交流电子负载时…

计算机相关专业是否仍是“万金油”的选择?

亲爱的朋友们&#xff1a; 2024 年高考已然落幕&#xff0c;数百万高三学子站在了人生的重要十字路口&#xff0c;面临着选择大学专业这一关键抉择。在这个节点上&#xff0c;计算机相关专业是否还能被称为“万金油”的选择呢&#xff1f; 相信大家都知道&#xff0c;在最近这几…

【前端项目笔记】2 主页布局

主页布局 element-ui提供的组件名称就是它的类名 ☆☆ CSS选择器&#xff1a; &#xff08;1&#xff09;基本选择器 类型选择器 p/span/div…… 类选择器 (.classname) ID选择器 (#idname) 通配选择器 ( * ) &#xff08;2&#xff09;属性选择器 选择具有特定属性或属性值的…

k8s删除状态为 Terminating 的pod

卸载calico pod时候pod资源状态会卡在terminating&#xff0c;这时候需要手动进行删除 使用以下命令即可 kubectl delete pod podName -n NAMESPACE --force --grace-period0记住一定要加命名空间&#xff0c;不然会报错没有找到

Android可穿戴设备世界之旅

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 介绍 Android通过在电视、穿戴和汽车等各种电子模块中扩展下一代应用开发概念&#xff0c;扩展了其整个范围和可…

计算机网络:6应用层

概述 客户/服务器方式和对等方式 客户/服务器&#xff08;Client/Server&#xff0c;C/S&#xff09;方式 客户和服务器是指通信中所涉及的两个应用进程。 客户/服务器方式所描述的是进程之间服务和被服务的关系。 服务器总是处于运行状态&#xff0c;并等待客户的服务请求。 …

C# + easyui 写的一个web项目

用C# easyui 来开发&#xff0c;其实就是为了开发速度&#xff0c;用easyui可以一天写很多页面&#xff0c;比一些低代码平台还快。 登陆页面 主界面 记录数统计 家庭信息采集表 新建家庭 家庭成员 低保、五保人员帮扶情况登记表 低保、五保人员帮扶情况登记表的新增和编辑 治…

【星海随笔】云解决方案学习日志篇(二) kafka、Zookeeper、Fielbeat

Elastic 中国社区官方博客 https://blog.csdn.net/ubuntutouch/category_9209092.html Kafka kafka的源代码是基于Scala语言编写的&#xff0c;运行在Java虚拟机&#xff08;即:JVM&#xff09;上。因此&#xff0c;在安装kafka之前需要先安装JDK Kafka 为什么依赖 Zookeepe…

数据库、中台、报表平台之间的关系

我最近在接触报表平台和中台&#xff0c;发现他们跟我平常用的数据库不是一个东西。然后&#xff0c;我开始了摸索他们的过程&#xff0c;终于&#xff0c;我在理清他们的关系以后&#xff0c;简单写一个入门级的区分。 数据库&#xff1a; 定义&#xff1a; 数据库是被长期存…

HarmonyOS开发日记 :自定义节点,实现 UI 组件 动态创建、更新

引言 UI动态操作包含组件的动态创建、卸载、更新等相关操作。 通过组件预创建&#xff0c;可以满足开发者在非build生命周期中进行组件创建&#xff0c;创建后的组件可以进行属性设置、布局计算等操作。之后在页面加载时进行使用&#xff0c;可以极大提升页面响应速度。 UI …

S32K3通过S32DS实现:S32K3如何将FLASH驱动放到RAM里面、RAM如何实现软件复位数据不丢失操作。

目录 1、概述 2、默认flash存放位置展示 3、通过默认的链接文件将flash放置到RAM 4、通过修改启动与链接文件将flash放在RAM 5、RAM热复位数据不丢失 1、概述 在通过RTD的SDK也好MCAL也好,始终存在一个问题,生成的代码除了看门狗模块,默认都是放在flash里面,按照正常逻…