C++相关概念和易错语法(17)(适配器模式、仿函数)

news2024/11/16 13:47:38

1.stack和queue

stack和queue的相关接口如下:

stack

queue

我们发现不管是stack还是queue,它们都有push和pop,不区分push_back和push_front,这是由它们的入栈特定顺序特性决定的,并且它们都没有迭代器,stack只有top,queue只有front和back这使得我们只能以规定的方式去访问这个stack或者queue

emplace相当于入栈,一般我们用push就可以了。

2.stack和queue的模拟实现

我们已经知道stack、queue和vector、list本质上并没有区别,所以我们可以将复用的思想带到它们的实现中,并且我们可以利用模板对于同一个stack模板,使用不同的底层来实现。这体现了继迭代器模式后的第二种设计模式:适配器模式,即转换。

下面是它们的实现

template<class T, class Container = vector<T>>
class stack
{
public:
	stack(const Container& con = Container())
		:_con(con)
	{}

	void push(const T& val)
	{
		_con.push_back(val);
	}		
		
	void pop()
	{
		_con.pop_back();
	}		
		
	T& top()
	{
		return _con.back();
	}

	size_t size()
	{
		return _con.size();
	}

	bool empty()
	{
		return _con.empty();
	}

	void swap(stack& st)
	{
		std::swap(_con, st._con);
	}


private:
	Container _con;
};
	
template<class T, class Container = list<T>>
class queue
{
public:
	queue(const Container& con = Container())
		:_con(con)
	{}

	void push(const T& val)
	{
		_con.push_back(val);
	}		
		
	void pop()
	{
		_con.pop_front();
	}		
		
	T& back()
	{
		return _con.back();
	}		
		
	T& front()
	{
		return _con.front();
	}

	size_t size()
	{
		return _con.size();
	}

	bool empty()
	{
		return _con.empty();
	}

	void swap(queue& q)
	{
		std::swap(_con, q._con);
	}

我们可以看出,stack和queue的实现几乎不需要自己实现具体功能,我们只需要关注上层功能而不是底层的细节,这极大地体现了封装的特性。

代码中仍有一些细节点需要注意:

(1)为什么只写了构造但没有写析构?为什么构造有缺省值

构造时我们可能空构造,需要缺省值,也有可能带参构造,带参构造需要我们形参接收,这里展示的是拷贝构造的情况,析构的时候成员_con作为自定义类型会去调用自己的析构函数,不会存在内存泄露的情况,所以不需要我们显式的去写析构

(2)swap为什么使用std::swap?

事实上,当我们包含了<vector><list>后,std域里的swap函数就多了几个重载,这几个重载是专门针对vector和list类型的,_con和q._con是Container类型,也就是vector<int>或list<int>类型,会去调用重载的效率较高的swap函数。

之所以std里重载swap函数,就是为了让我们在不知vector类里有swap成员函数的情况下也能高效地交换(只交换指针的值而不是交换所有指向的值)。因此除了这种写法,我们还可以显式调用vector类的成员函数

(3)stack只能以vector作为容器吗?

vector是连续的物理结构,下标访问快,但头插效率极低。因此vector不支持头插头删。在queue中pop使用了pop.front(),在vector里没有这个接口,所以不能使用vector实现queue,但是我们如果用erase来实现pop也可以,这样就可以用vector实现queue了,但并不建议这么做,因为vector实现queue本就是效率极低的做法。

list是分散的物理结构,任意位置插入删除效果都不错,但下标访问效率低。因此list不支持下标访问。但是stack和queue都没用到下标访问,因此我们可以用list实现queue和stack。

事实上,stack和queue在STL中都是以deque作为默认的容器。我们发现,vector和list本就是两个极端的存储结构。所以deque是结合vector、list,能同时进行下标访问和push_back、push_front等操作的容器。

deque用buff小数组+中控数组来实现它的功能,是vector和list存储的结合体,虽然支持下标访问,但它的没vector快,deque[]偶尔用用还行,头尾删除不错。我们适当了解一下deque即可,不用深究,因为deque本不算一个效率高的容器。

3.优先级队列和仿函数

priority_queue本质上是堆,我们只要掌握了堆,优先级队列的核心就能很快完成了。

但是priority_queue用到了仿函数的知识点,在大堆小堆的处理和我们C语言有所不同

(1)仿函数:

仿函数,即模仿函数的一个类,我们看下面的代码:


comp(1,2)是我们常见的函数的写法,但它的本质是是一个类重载了operator()后表现出来的类似于函数的特性。仿函数实现起来也相当简单,只要理解了它的实质就能很快上手

但是仿函数和构造函数容易搞混,一定要注意仿函数是要针对已创建的对象调用而不是针对类来调用,那种写法是构造函数而不是仿函数

(2)优先级队列

优先级队列的功能很简单,就是结合vector、仿函数实现堆,核心代码就是AdjustUp和AdjustDown。

建议将AdjustUp和AdjustDown放进private,对外只提供push、pop这样的接口,体现封装的特性。

注意当仿函数是less时默认是大堆,greater默认是小堆,在我们进行数值比较时要注意谁在前谁在后,因为顺序不同,会影响返回值,从而影响我们生成的堆的类型。

由于堆相关的知识前面我已经详细讲过,这里就不多阐述了。

完整代码如下:


namespace my_priority_queue
{
	template<typename T>
	class less
	{
	public:
		bool operator()(const T& t1, const T& t2)
		{
			return t1 < t2;
		}
	};	
	
	
	template<typename T>
	class greater
	{
	public:
		bool operator()(const T& t1, const T& t2)
		{
			return t1 > t2;
		}
	};


	template<typename T, class Container = vector<T>, class Compare = less<T>>
	class priority_queue
	{
	public:

		priority_queue() = default;

		template<typename iterator>
		priority_queue(iterator begin, iterator end)
		{
			while (begin != end)
			{
				_con.push_back(*begin);
				begin++;
			}

			for (int parent = (int)(size() - 2) / 2; parent >= 0; parent--)
			{
				AdjustDown(parent);
			}

		}

		size_t size()
		{
			return _con.size();
		}

		void push(const T& val)
		{
			_con.push_back(val);
			AdjustUp(size() - 1);
		}

		void pop()
		{
			std::swap(_con[0], _con[size() - 1]);
			_con.pop_back();
			AdjustDown(0);
		}

		const T& top()
		{
			return _con[0];
		}

		bool empty()
		{
			return _con.empty();
		}

		void swap(priority_queue& p)
		{
			std::swap(_con, p._con);
		}



	private:
		void AdjustUp(size_t child)
		{
			Compare comp;

			size_t parent = (child - 1) / 2;

			while (child > 0)
			{
				if (comp(_con[parent], _con[child]))
					std::swap(_con[parent], _con[child]);

				child = parent;
				parent = (child - 1) / 2;
			}
		}

		void AdjustDown(size_t parent)
		{
			Compare comp;

			size_t child = parent * 2 + 1;

			while (child < size())
			{
				if (child + 1 < size() && comp(_con[child], _con[child + 1]))
					child++;

				if(comp(_con[parent], _con[child]))
					std::swap(_con[parent], _con[child]);

				parent = child;
				child = parent * 2 + 1;
			}
		}


	private:
		Container _con;
	};
}

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

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

相关文章

【系统架构设计】计算机组成与体系结构(一)

计算机组成与体系结构 计算机系统组成计算机硬件组成控制器运算器主存储器辅助存储器输入设备输出设备 计算机系统结构的分类存储程序的概念Flynn分类 复杂指令集系统与精简指令集系统总线 存储器系统流水线 兜兜转转&#xff0c;最后还是回到了4大件&#xff0c;补基础&#x…

【力扣】数组中的第K个最大元素

一、题目描述 给定整数数组 nums 和整数 k&#xff0c;请返回数组中第 k 个最大的元素。 请注意&#xff0c;你需要找的是数组排序后的第 k 个最大的元素&#xff0c;而不是第 k 个不同的元素。 你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例 1: 输入: [3,2,1,5,…

图片像素坐标转实际坐标的一种转换方案

原图 红色的点是我们标注的像素点&#xff0c;这些红色的点我们知道它的像素坐标&#xff0c;以及以右下角相机位置为原点的x y 实际坐标数值 通过转换&#xff0c;可以得到整个图片内部其余像素点的实际坐标&#xff0c; 这些红色的点是通过转换关系生成的&#xff0c;每隔一米…

python破解密码·筛查和选择

破解密码时可能遇到的几种情况 ① 已知密码字符&#xff0c;破排序 ② 已知密码位数&#xff0c;破字符 ③ 已知密码类型&#xff0c;破字位 ④ 已知部分密码&#xff0c;破未知 ⑤ 啥都不知道&#xff0c;盲破&#xff0c;玩完 ⑥ 已知位数、字符、类型、部分密码中的几个&am…

2024全网最全面及最新且最为详细的网络安全技巧五 之 SSRF 漏洞EXP技巧,典例分析以及 如何修复 (下册)———— 作者:LJS

五.SSRF 漏洞EXP技巧&#xff0c;典例分析以及 如何修复 (下册) 目录 五.SSRF 漏洞EXP技巧&#xff0c;典例分析以及 如何修复 (下册) 5.4gopher 协议初探 0x01 Gopher协议 0x02 协议访问学习 复现环境 centos7 kali 2018 发送http get请求 发送http post请求 5.5 SSRF…

☺初识c++(语法篇)☺

目录 一命名空间&#xff08;namespace&#xff09;&#xff1a; 二cout与cin简述&#xff1a; 三缺省参数&#xff1a; 四函数重载&#xff1a; 五引用&#xff1a; 六内联函数: 七c中的nullptr简述&#xff1a; 一命名空间&#xff08;namespace&#xff09;&#xff1…

Chromium编译指南2024 Linux篇-同步Chromium第三方库(四)

1.引言 在成功拉取Chromium源码并创建新分支后&#xff0c;我们需要进一步配置开发环境。这包括拉取必要的第三方库以及设置hooks&#xff0c;以确保我们能够顺利进行编译和开发工作。以下步骤将详细介绍如何进行这些配置。 2.拉取第三方库以及hooks Chromium 使用了大量的第…

2024第六届上海国际新材料展览会-12月精彩呈现

2024第六届上海国际新材料展览会 The 6th shanghai International New Materials Exhibition in 2024 时 间&#xff1a;2024年12月18-20日 地 点&#xff1a;上海新国际博览中心 CIME 2024专业、权威&#xff0c;涵盖整个新材料行业的国际盛会。 期待与您在CIME 2024现场相…

24-7-9-读书笔记(九)-《爱与生的苦恼》[德]叔本华 [译]金玲

文章目录 《爱与生的苦恼》阅读笔记记录总结 《爱与生的苦恼》 《爱与生的苦恼》叔本华大佬的名书&#xff0c;里面有其“臭名昭著”的《论女人》&#xff0c;抛开这篇其他的还是挺不错的&#xff0c;哲学我也是一知半解&#xff0c;这里看得也凭喜好&#xff0c;这里记录一些自…

大模型/NLP/算法面试题总结2——transformer流程//多头//clip//对比学习//对比学习损失函数

用语言介绍一下Transformer的整体流程 1. 输入嵌入&#xff08;Input Embedding&#xff09; 输入序列&#xff08;如句子中的单词&#xff09;首先通过嵌入层转化为高维度的向量表示。嵌入层的输出是一个矩阵&#xff0c;每一行对应一个输入单词的嵌入向量。 2. 位置编码&…

Qt 创建的窗口一闪而过【已解决】

Qt 创建的窗口一闪而过 引言一、详细的解决方案 - 附代码二、参考博文 引言 创建的窗口一闪而过&#xff0c;就是创建完立马被销毁了&#xff0c;常见情况是在一个函数中创建窗口并show() - 即创建在了栈上&#xff0c;函数结束局部变量(窗口)自动被释放。主流的解决方法有两种…

(HAL)stm32f407+freertos通过usb驱动移远4G模块-EC600U

概述 本篇文章主要介绍: 如何使用STM32CubeMX创建stm32F407+freertos+usb host的基础工程。USB-HOST-CDC驱动运行过程。如何根据4G模块的具体信息修改usb相关代码。MCU如何通过usb与4G模块通信,收发数据。调试过程中遇到的问题以及解决办法。 整个过程中在网上搜罗了很多参考…

机场的出租车问题折线图

分析并可视化机场离场车辆数数据 本文将详细介绍如何使用Python的正则表达式库re和绘图库matplotlib对机场离场车辆数数据进行分析和可视化。以下是具体步骤和代码实现。 数据资源&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1rU-PRhrVSXq-8YdR6obc6Q?pwd1234 提…

干货:高水平论文写作思路与方法

前言:Hello大家好,我是小哥谈。高水平论文的写作需要扎实的研究基础和严谨的思维方式。同时,良好的写作技巧和时间管理也是成功的关键。本篇文章转载自行业领域专家所写的一篇文章,希望大家阅读后可以能够有所收获。🌈 目录 🚀1.依托事实/证据,通过合理的逻辑,…

每日刷题(二分查找,匈牙利算法,逆序对)

目录 1.Sarumans Army 2.Catch That Cow 3.Drying 4.P3386 【模板】二分图最大匹配 5. Swap Dilemma 1.Sarumans Army 3069 -- Sarumans Army (poj.org) 这道题就是要求我们在给的的位置放入 palantir&#xff0c;每个 palantir有R大小的射程范围&#xff0c;要求求出最少…

C# Bitmap类型与Byte[]类型相互转化详解与示例

文章目录 一、Bitmap类型转Byte[]类型使用Bitmap类的Save方法使用Bitmap类的GetBytes方法 二、Byte[]类型转Bitmap类型使用MemoryStream将Byte[]数组转换为Bitmap对象使用System.Drawing.Imaging.BitmapImage类 总结 在C#编程中&#xff0c;Bitmap类型和Byte[]类型之间的相互转…

【刷题汇总 -- 求最小公倍数、数组中的最长连续子序列、字母收集】

C日常刷题积累 今日刷题汇总 - day0081、求最小公倍数1.1、题目1.2、思路1.3、程序实现 -- 穷举法1.2、程序实现 -- 辗转相除法 2、数组中的最长连续子序列2.1、题目2.2、思路2.3、程序实现 3、字母收集3.1、题目3.2、思路3.3、程序实现 4、题目链接 今日刷题汇总 - day008 1、…

01:简易的电动车防盗报警器

简易的电动车防盗报警器 1、震动传感器模块的使用2、使用震动传感器模块控制继电器开关3、433M无线发射接收模块的使用 需要材料&#xff1a; 1、51单片机 2、震动传感器模块 3、继电器模块 4、高功率喇叭 5、433M无线发射接收模块 6、弱干杜邦线 1、震动传感器模块的使用 接好…

13 - matlab m_map地学绘图工具基础函数 - 介绍创建管理颜色映射的函数m_colmap和轮廓图绘制颜色条的函数m_contfbar

13 - matlab m_map地学绘图工具基础函数 - 介绍创建管理颜色映射的函数m_colmap和轮廓图绘制颜色条的函数m_contfbar 0. 引言1. 关于m_colmap2. 关于m_contfbar3. 结语 0. 引言 本篇介绍下m_map中用于创建和管理颜色映射函数&#xff08;m_colmap&#xff09;和 为轮廓图绘制颜…

大话光学原理:2.最短时间原理、“魔法石”与彩虹

一、最短时间原理 1662年左右&#xff0c;费马在一张信纸的边角&#xff0c;用他那著名的潦草笔迹&#xff0c;随意地写下了一行字&#xff1a;“光在两点间选择的路&#xff0c;总是耗时最少的。”这句话&#xff0c;简单而深邃&#xff0c;像是一颗悄然种下的种子&#xff0c…