特殊类设计[下] --- 单例模式

news2024/11/19 22:47:19

文章目录

  • 5.只能创建一个对象的类
    • 5.1设计模式[2.5 万字详解:23 种设计模式](https://zhuanlan.zhihu.com/p/433152245)
    • 5.2单例模式
      • 1.饿汉模式
      • 1.懒汉模式
  • 6.饿汉模式
  • 7.懒汉模式
    • 7.1饿汉模式优缺点:
    • 7.2懒汉模式
      • 1.线程安全问题
      • 2.单例对象的析构问题
  • 8.整体代码
  • 9.C++11后可用的单例模式

5.只能创建一个对象的类

5.1设计模式2.5 万字详解:23 种设计模式

在这里插入图片描述

设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结

为什么会产生设计模式这样的东西呢?

就像人类历史发展会产生兵法。最开始部落之间打仗时都是人拼人的对砍。春秋战国时期,七国之间经常打仗,发现打仗也是有套路的,孙子就总结出了《孙子兵法》

使用设计模式的目的是什么呢?

代码可重用性、代码更容易被理解、代码可靠性、代码编写工程化

设计模式是软件工程的基石脉络,如同大厦的结构一样

5.2单例模式

在这里插入图片描述

一个类只能创建一个对象,即单例模式

该模式可以保证系统中(一个进程)该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。[在此进程全局只有唯一一个 且 在任意地方可访问]

应用场景

在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,服务进程中的其他对象通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理

1.饿汉模式

1.懒汉模式

6.饿汉模式

程序启动时(main函数之前)就创建一个唯一的实例对象

class Singleton
{
public:
	static Singleton* GetPtrAtOnly()
	{
		return _ponly;
	}

	void PushData(const string& str)
	{
		_mtx.lock();

		_vec.push_back(str);

		_mtx.unlock();
	}

	void Display()
	{
		_mtx.lock();

		for (auto& e : _vec)
		{
			cout << e << endl;
		}
		cout << endl;

		_mtx.unlock();
	}

private:
	//构造函数私有化 -- 禁止类外创建对象
	Singleton()
	{
	
	}

private:
	mutex _mtx;
	vector<string> _vec;

	//_ponly是一个存在于静态区的指针变量
	//这个指针初始化指向 一个Singleton对象
	static Singleton* _ponly;
};

//在程序入口之前就完成单例对象的初始化
//类内声明 类外初始化
Singleton* Singleton::_ponly = new Singleton;

int main()
{
	//Singleton s1;
	//static Singleton s2;
	//Singleton* p = new Singleton;

	Singleton::GetPtrAtOnly()->PushData("彭于晏");
	Singleton::GetPtrAtOnly()->PushData("吴彦祖");
	Singleton::GetPtrAtOnly()->PushData("黎明");
	Singleton::GetPtrAtOnly()->PushData("郭富城");
	Singleton::GetPtrAtOnly()->Display();
	return 0;
}

在这里插入图片描述
在这里插入图片描述

多线程单例模式之饿汉模式测试

int main()
{
	srand(time(0));

	int n = 10;
	thread t1([n]()
		{
			for (size_t i = 0; i < n; ++i)
			{
				Singleton::GetPtrAtOnly()->PushData("线程1: " + to_string(rand()));
			}
		}
	);

	thread t2([n]()
		{
			for (size_t i = 0; i < n; ++i)
			{
				Singleton::GetPtrAtOnly()->PushData("线程2: " + to_string(rand()));
			}
		}
	);

	t1.join();
	t2.join();

	Singleton::GetPtrAtOnly()->Display();

	return 0;
}

在这里插入图片描述

7.懒汉模式

7.1饿汉模式优缺点:

优点:相对懒汉模式而言简单一些
缺点:

  1. 影响进程启动速度
    饿汉模式main函数之前就要创建对象
    若单例对象初始化很慢(初始化操作很多[读取配置文件]) 对象1暂时不占用资源
    但是会影响后续程序的启动速度
  2. 多个单例类对象 实例启动顺序不确定
    两个有依赖关系的单例都是饿汉时
    若要求创建顺序:单例1--单例2
    饿汉模式无法控制顺序

7.2懒汉模式

1.线程安全问题

  1. 懒汉模式
static Singleton* GetPtrAtOnly()
{
	if (_ponly == nullptr)
	{
		if (_ponly == nullptr)
		{
			_ponly = new Singleton;
		}
	}
	return _ponly;
}

假设两个线程 线程1的对象实例化后进行了添加数据 此时线程2执行 覆盖线程1

  1. 饿汉模式不用考虑

线程在main函数后进行 饿汉模式在main函数前就创建了对象

    static Singleton* GetPtrAtOnly()
	{
		return _ponly;
	}
	
Singleton* Singleton::_ponly = new Singleton;

加锁保护

	static Singleton* GetPtrAtOnly()
	{
	    _imtx.lock();
		if (_ponly == nullptr)
		{
			_ponly = new Singleton;
		}
		_imtx.unlock();

		return _ponly;
	}

每次创建对象都要 加锁解锁 有无改进办法?

	static Singleton* GetPtrAtOnly()
	{
		if (_ponly == nullptr)
		{
			_imtx.lock();
				_ponly = new Singleton;
			_imtx.unlock();
		}
		return _ponly;
	}

此时相当于没加锁 跟没加锁造成的问题一样 以下的双检查加锁才是解决办法

	static Singleton* GetPtrAtOnly()
	{
	    //懒汉模式 不在外部加锁 提高效率 -- 要不然每次创建对象都要加锁
		if (_ponly == nullptr)
		{
			_imtx.lock();
			
            //线程安全 t1判断为空 new对象 t2来了不为空 不再new 更正了覆盖问问题
			if (_ponly == nullptr)
			{
				_ponly = new Singleton;
			}

			_imtx.unlock();
		}

		return _ponly;
	}

2.单例对象的析构问题

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

8.整体代码

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <list>
#include <vector>
#include <algorithm>
#include <array>
#include <time.h>
#include <queue>
#include <stack>
#include <string>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map> 
#include <thread> 
#include <functional>
#include <assert.h>
#include<mutex>
using namespace std;


// 饿汉模式:
/*
class Singleton
{
public:
	static Singleton* GetPtrAtOnly()
	{
		return _ponly;
	}

	void PushData(const string& str)
	{
		_mtx.lock();

		_vec.push_back(str);

		_mtx.unlock();
	}

	void Display()
	{
		_mtx.lock();

		for (auto& e : _vec)
		{
			cout << e << endl;
		}
		cout << endl;

		_mtx.unlock();
	}

private:
	//构造函数私有化 -- 禁止类外创建对象
	Singleton()
	{

	}

private:
	mutex _mtx;
	vector<string> _vec;

	//_ponly是一个存在于静态区的指针变量
	//这个指针初始化指向 一个Singleton对象

	//这里可以直接static Singleton _only; 他是一个对象 程序结束时调用析构
	//而懒汉模式只能是指针因为他要判断是否空再去创建对象
	//所以懒汉模式不得不写一个对象回收实现自动析构
	static Singleton* _ponly;
};
Singleton* Singleton::_ponly = new Singleton;
*/

//懒汉模式:第一次访问实例对象时[第一次调用GetPtrAtOnly()]创建
class Singleton
{
public:
	//获取单例对象
	static Singleton* GetPtrAtOnly()
	{
		if (_ponly == nullptr)
		{
			_ptrmtx.lock();

			if (_ponly == nullptr)
			{
				_ponly = new Singleton;
			}

			_ptrmtx.unlock();
		}

		return _ponly;
	}

	// 一般全局都要使用单例对象 
	// 所以单例对象一般不需要显示释放  
	// 特殊场景 -- 显示释放

	//释放单例对象
	static void DeletePtrAtOnly()
	{
		_ptrmtx.lock();
		if (_ponly != nullptr)
		{
			delete _ponly;
			_ponly = nullptr;
		}
		_ptrmtx.unlock();
	}


	void PushData(const string& str)
	{
		_vecmtx.lock();

		_vec.push_back(str);

		_vecmtx.unlock();
	}

	void Display()
	{
		_vecmtx.lock();

		for (auto& e : _vec)
		{
			cout << e << endl;
		}
		cout << endl;

		_vecmtx.unlock();
	}

	~Singleton()
	{
		// 要求程序结束时
		// 将数据写到文件 
		// 单例对象析构时[持久化]
		// 即析构前做事情 

		// 写文件操作
		//DeletePtrAtOnly();

		//存在一种情况 写文件操作代码量太大 最后忘记调用DeletePtrAtOnly();
		//此时有没有析构单例对象 怎么办? 能不能搞得智能一点?

		//类比智能指针 再搞一个类 使得实现"自动化"

		//_gc是一个静态局部变量 他的析构发生在main函数结束后 程序结束时
		//_gc析构时 会调用他的析构函数~Garbage_Collection(); 
		//他的析构时会调用单例对象的析构函数 由此实现自动化
	}

	// 单例对象回收
	class Garbage_Collection
	{
	public:
		~Garbage_Collection()
		{
			DeletePtrAtOnly();
		}
	};
	static Garbage_Collection _gc;

private:
	Singleton()
	{
	
	}

	//有锁时 不禁用拷贝构造也行 因为锁使得vector不能push_back
	Singleton(const Singleton& s) = delete;
	Singleton& operator=(const Singleton& s) = delete;
private:
	mutex _vecmtx;
	vector<string> _vec;

	static mutex _ptrmtx;
	static Singleton* _ponly;
};

mutex Singleton::_ptrmtx;

Singleton* Singleton::_ponly = nullptr;

Singleton::Garbage_Collection Singleton::_gc;

int main()
{
	//Singleton s(*Singleton::GetPtrAtOnly());

	srand(time(0));

	int n = 20;
	thread t1([n]()
		{
			for (size_t i = 0; i < n; ++i)
			{
				Singleton::GetPtrAtOnly()->PushData("线程1: " + to_string(rand()));
			}
		}
	);

	thread t2([n]()
		{
			for (size_t i = 0; i < n; ++i)
			{
				Singleton::GetPtrAtOnly()->PushData("线程2: " + to_string(rand()));
			}
		}
	);

	t1.join();
	t2.join();

	Singleton::GetPtrAtOnly()->Display();

	return 0;
}

9.C++11后可用的单例模式

C++11单例模式简单写法:将对象定义GetPtrAtOnly()函数的局部静态变量 返回对象的引用 在GetPtrAtOnly()函数首次调用时完成静态对象初始化
当某一个线程调用GetPtrAtOnly()执行初始化静态变量时,若其他线程正在执行初始化该静态变量 则先初始化上一进程

class Singleton
{
public:
    // C++11后才可以保证初始化静态对象的线程安全问题
	static Singleton* GetPtrAtOnly()
	{
		static Singleton one; 
		return &one;
	}

	void PushData(const string& str)
	{
		_vecmtx.lock();

		_vec.push_back(str);

		_vecmtx.unlock();
	}

	void Display()
	{
		_vecmtx.lock();

		for (auto& e : _vec)
		{
			cout << e << endl;
		}
		cout << endl;

		_vecmtx.unlock();
	}

	~Singleton()
	{

	}
private:
	Singleton(){}
	Singleton(const Singleton& s) = delete;
	Singleton& operator=(const Singleton& s) = delete;

	mutex _vecmtx;
	vector<string> _vec;
};

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++项目——云备份-⑤-数据管理模块的设计与实现

文章目录 专栏导读1.要管理的数据有哪些2.如何管理数据3.数据信息结构体设计与实现4.数据管理类设计5.数据管理类实现6.数据管理模块整理 专栏导读 &#x1f338;作者简介&#xff1a;花想云 &#xff0c;在读本科生一枚&#xff0c;C/C领域新星创作者&#xff0c;新星计划导师…

打破尺寸记录!荷兰QuTech研发16量子点阵列新技术

承载16个量子点交叉条阵列的量子芯片&#xff0c;可无缝集成到棋盘图案&#xff08;图片来源&#xff1a;网络&#xff09; 由荷兰代尔夫特理工大学(TU Delft)和荷兰应用科学研究组织(TNO)组建的荷兰量子计算研究中心QuTech的研究人员开发了一种用相对较少的控制线来控制大量量…

【QT】Qt控件不显示图标

问题描述 本人在跟着B站视频学习QT时&#xff0c;遇到了一件十分悲惨的事情&#xff0c;一模一样的步骤&#xff0c;我的图标却不能显示。 于是我上网查询一下解决方案&#xff0c;第一种&#xff0c;亲测没用&#xff1b;第二种亲测可以。 解决方法一 1、构建 -> 清理项目…

实战CubeMX配置CAN通讯教程,避免踩坑,cubeMX 回环模式可以但正常模式无法通信

文章目录 实战CubeMX配置CAN通讯教程&#xff0c;避免踩坑&#xff0c;cubeMX 回环模式可以但正常模式无法通信1. 先配置两个LED等的普通IO口&#xff0c;作为通信指示信号2.配置时钟单元3.配置工程文件4.配置代码生成的参数5.配置CAN通信的波特率&#xff0c;注意如果配置成50…

成都瀚网科技有限公司:抖音小店收益计算大揭秘,一招提升你的利润!

你是否曾对抖音小店的收益计算方式感到困惑&#xff1f;想要了解如何提高抖音小店的收益吗&#xff1f;本文将为你揭开抖音小店收益计算的神秘面纱&#xff0c;并分享一些实用的提升利润的方法。 一、抖音小店收益计算方式 抖音小店的收益主要来自于商品销售收入、佣金收入以及…

0146 网络层

目录 4 网络层 4.1 网络层的功能 4.2 路由算法与路由协议 4.3 IPv4 4.4 IPv6 4 网络层 4.1 网络层的功能 4.2 路由算法与路由协议 4.3 IPv4 4.4 IPv6 部分习题 1.网络层的主要目的是&#xff08;&#xff09; A.在邻接结点间进行数据报传输 B.在邻接结点间进行数…

郑州职工注意!郑州市职工数字人才技能竞赛正式启动

10月26日&#xff0c;由郑州市劳动竞赛委员会办公室、郑州市总工会、郑州市大数据管理局、郑州市人力资源和社会保障局、郑州市科学技术局主办&#xff0c;郑东新区总工会、中科大数据研究院联合承办的郑州市职工数字人才技能竞赛在郑东新区顺利举行启动仪式。 河南省总工会副主…

『进阶之路』- 揭开ThreadLocal神秘面纱

阅读本文主要可以解决以下困惑&#xff1a; 什么是ThreadLocal&#xff0c;隔离线程的本地变量ThreadLocal的数据结构是怎么样的&#xff0c;为什么能实现线程隔离ThreadLocal的get和set方法ThreadLocal如何实现的线程安全&#xff1f;结合同步锁机制&#xff0c;空间换取时间…

2023年腾讯云双11服务器活动及价格表

双十一购物狂欢节即将到来&#xff0c;腾讯云作为国内领先的云计算服务提供商&#xff0c;推出了一系列优惠活动&#xff0c;下面给大家详细介绍腾讯云双11服务器活动及价格表。 一、腾讯云双11活动入口 活动入口&#xff1a;txy.ink/1111/ 二、腾讯云双11活动时间 即日起至…

智慧巡查平台(Ionic/Vite/Vue3 移动端) 问题记录

目录 1.环境搭建 1.1 安装 node 16 版本 1.2 安装 ionic7 1.3 创建 vue 项目 2.index.html 3.main.ts 3.1 如何默认使用 ios 样式&#xff1f; 3.2 如何使用 ElmentPlus 国际化&#xff1f; 4.router/xxx 5.打包二三事 5.1 添加打包相关文件 5.1.1 .env.developmen…

3、电路综合原理与实践---单双端口理想微带线(伪)手算S参数与时域波形

电路综合原理与实践—单双端口理想微带线&#xff08;伪&#xff09;手算S参数与时域波形与时域波形 1、单理想微带线&#xff08;UE&#xff09;的S参数理论推导 参考&#xff1a;Design of Ultra Wideband Power Transfer Networks的第四章&#xff0c;之后总结推导过程 自…

Kubernetes中如何使用CNI?

一、CNI 是什么 它的全称是 Container Network Interface&#xff0c;即容器网络的 API 接口。 它是 K8S 中标准的一个调用网络实现的接口。Kubelet 通过这个标准的 API 来调用不同的网络插件以实现不同的网络配置方式。实现了这个接口的就是 CNI 插件&#xff0c;它实现了一…

长连接的原理

Apollo的长连接实现是 Spring的DeferredResult来实现的,先看怎么用 import ...RestController RequestMapping("deferredResult") public class DeferredResultController {private Map<String, Consumer<DeferredResultResponse>> taskMap new HashMa…

如何恢复u盘删除文件?2023最新分享四种方法恢复文件

U盘上删除的文件怎么恢复&#xff1f;使用U盘存储文件是非常方便的&#xff0c;例如&#xff1a;在办公的时候&#xff0c;会使用U盘来存储网络上查找到的资料、产品说明等。在学习的时候&#xff0c;会使用U盘来存储教育机构分享的教学视频、重点知识等。而随着U盘存储文件的概…

[数据结构】二叉树

1.概念 一棵二叉树是结点的一个有限集合&#xff0c;该集合&#xff1a; 1. 或者为空 2. 或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成 从上图我们可以发现&#xff1a; 1.二叉树不存在大于2 的度 2.二叉树的子树有左右之分&#xff0c;次序不能颠倒。是有…

关于内存泄漏的经典面试题

目录 前言 一、内存泄漏基本概念 二、如何判断并查找内存泄漏 1、方案设计 2、方案实现 前言 对于C/C程序员来说&#xff0c;或多或少都会被面试官问到关于内存泄漏的问题&#xff0c;内存泄漏是程序的bug&#xff0c;他会一点一点的侵蚀你的内存&#xff0c;导致程序运行…

jmeter报Java.NET.BindException: Address already in use: connect

1、windows10和window11上&#xff1a; 修改注册表的内容&#xff1a; HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters&#xff1a; 新建dword&#xff08;值&#xff09;的类型&#xff1a; MaxUserPort 65334 TcpTimedWaitDelay 30window

2023年Q3线上生鲜水产数据分析:市场不景气,销额同比下滑44%

事实上&#xff0c;今年线上的生鲜生意市场并不景气。无论是Q1季度还是Q2季度&#xff0c;线上&#xff08;京东平台&#xff09;的销售额均同比去年下滑了10%左右。 然而到了Q3季度&#xff0c;整个下滑态势愈发严峻。可以看到鲸参谋数据显示&#xff0c;7月至9月生鲜水产在京…

腾讯云学生专享云服务器介绍及购买攻略

随着互联网技术的不断发展&#xff0c;越来越多的人开始关注云计算领域。作为国内领先的云计算服务商&#xff0c;腾讯云推出了“云校园”扶持计划&#xff0c;完成学生认证即可购买学生专享云服务器。 一、活动对象 仅限腾讯云官网通过个人认证的35岁以下学生用户参与&#x…

数据结构笔记——树和图(王道408)(持续更新)

文章目录 传送门前言树&#xff08;重点&#xff09;树的数据结构定义性质 二叉树的数据结构定义性质储存结构 二叉树算法先中后序遍历层次展开法递归模拟法 层次遍历遍历序列逆向构造二叉树 线索二叉树&#xff08;难点&#xff09;定义线索化的本质 二叉树线索化线索二叉树中…