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

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;
	};
}

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

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

相关文章

倒计时1天!飞思实验室暑期公益培训,7月10日不见不散

01培训背景 很荣幸地向大家宣布&#xff1a;卓翼飞思实验室将于7月10日正式开启为期两个月的暑期公益培训&#xff01;本次培训为线上直播&#xff0c;由中南大学计算机学院特聘副教授&#xff0c;RflySim平台总研发负责人戴训华副教授主讲。 培训将基于“RflySim—智能无人集…

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

目录 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;要求求出最少…

jitsi 使用JWT验证用户身份

前言 Jitsi Meet是一个很棒的会议系统,但是默认他运行所有人创建会议,这样在某种程度上,我们会觉得他不安全,下面我们就来介绍下使用JWT来验证用户身份 方案 卸载旧的lua依赖性sudo apt-get purge lua5.1 liblua5.1-0 liblua5.1-dev luarocks添加ubuntu的依赖源,有则不需…

住宅代理、移动代理和数据中心代理之间的区别

如果您是一名认真的互联网用户&#xff0c;可能需要反复访问某个网站或服务器&#xff0c;可能是为了数据抓取、价格比较、SEO 监控等用例&#xff0c;而不会被 IP 列入黑名单或被 CAPTCHA 阻止。 代理的工作原理是将所有传出数据发送到代理服务器&#xff0c;然后代理服务器将…

UML 2.5图的分类

新书速览|《UML 2.5基础、建模与设计实践》新书速览|《UML 2.5基础、建模与设计实践 UML 2.5在UML 2.4.1的基础上进行了结构性的调整&#xff0c;简化和重新组织了 UML规范文档。UML规范被重新编写&#xff0c;使其“更易于阅读”&#xff0c;并且“尽可能减少前向引用”。 U…

Mock 测试技术

一、Mock 类框架的使用场景 在实际软件开发中&#xff0c;要进行测试的方法存在外部依赖&#xff08;如 db&#xff0c;redis&#xff0c;第三方接口调用等&#xff09;&#xff0c;这些外部依赖可能存在各种问题&#xff0c;例如不稳定、缺乏数据、难以模拟等等&#xff0c;所…

C++中的多重继承和虚继承:横向继承、纵向继承和联合继承;虚继承

多重继承 A.横向多重继承&#xff1a; B.纵向多重继承&#xff1a; C.联合多重继承&#xff1a; 因为 single 和 waiter 都继承了一个 worker 组件&#xff0c;因此 SingingWaiter 将包含两个 worker 组件&#xff0c;那么将派生类对象的地址赋给基类指针将出现二义性 那么如何…

使用F1C200S从零制作掌机之debian文件系统完善NES

一、模拟器源码 源码&#xff1a;https://files.cnblogs.com/files/twzy/arm-NES-linux-master.zip 二、文件系统 文件系统&#xff1a;debian bullseye 使用builtroot2018构建的文件系统&#xff0c;使用InfoNES模拟器存在bug&#xff0c;搞不定&#xff0c;所以放弃&…

Git 快速上手

这个文档适用于需要快速上手 Git 的用户&#xff0c;本文尽可能的做到简单易懂 ❤️❤️❤️ git 的详细讲解请看这篇博客 Git 详解&#xff08;原理、使用&#xff09; 1. 什么是 Git Git 是目前最主流的一个版本控制器&#xff0c;并且是分布式版本控制系统&#xff0c;可…

k8s中port,targetPort,nodePort,containerPort的区别

一、说明 在 Kubernetes 中&#xff0c;port、targetPort、nodePort 和 containerPort 是用于定义服务&#xff08;Service&#xff09;和容器之间网络通信的不同参数。 它们各自的作用和含义如下&#xff1a; 1. port 定义&#xff1a;这是服务对外暴露的端口号。作用&#x…

树莓派_Pytorch学习笔记20:初步认识深度学习框架

今日继续学习树莓派4B 4G&#xff1a;&#xff08;Raspberry Pi&#xff0c;简称RPi或RasPi&#xff09; 本人所用树莓派4B 装载的系统与版本如下: 版本可用命令 (lsb_release -a) 查询: ​ Python 版本3.7.3&#xff1a; ​ 本文很水&#xff0c;就介绍一下我以后的学习使用P…

【JavaEE】 简单认识CPU

&#x1f435;本篇文章将对cpu的相关知识进行讲解 一、认识CPU 下图是简略的冯诺依曼体系结构图 上图中&#xff0c;存储器用来存储数据&#xff0c;注意在存储器中都是以二进制的形式存储数据的&#xff0c;CPU就是中央处理器&#xff0c;其功能主要是进行各种算术运算和各种…

C++·模板进阶

1. 非类型模板参数 之前我们写的模板参数都设定class类型的&#xff0c;这个模板参数用来给下面的代码中的某些元素定义类型&#xff0c;我们管这种模板参数叫类型形参。非类型模板参数就是用一个常量作为模板的一个参数&#xff0c;在模板中可将该参数当作常量来使用&#xff…

tk Text文本框赋值,清空

import tkinter as tk# 创建主窗口 root tk.Tk() root.title("文本框内容赋值示例")# 创建一个Text小部件 text_area tk.Text(root, height10, width50) text_area.pack()# 将内容赋值给Text小部件 text_area.insert(tk.END, "这是文本框中的内容。\n")#…

STL--栈(stack)

stack 栈是一种只在一端(栈顶)进行数据插入(入栈)和删除(出栈)的数据结构,它满足后进先出(LIFO)的特性。 使用push(入栈)将数据放入stack,使用pop(出栈)将元素从容器中移除。 使用stack,必须包含头文件: #include<stack>在头文件中,class stack定义如下: namespace std…

前端面试题33(实时消息传输)

前端实时传输协议主要用于实现实时数据交换&#xff0c;特别是在Web应用中&#xff0c;它们让开发者能够构建具有实时功能的应用&#xff0c;如聊天、在线协作、游戏等。以下是几种常见的前端实时传输协议的讲解&#xff1a; 1. Short Polling (短轮询) 原理&#xff1a;客户…

k8s record 20240705

k8s 安全管理 request 是1g&#xff0c;你得不到要求&#xff0c;我就不创建了&#xff0c;这就是准入控制二次校验 SA就是serviceAccount。 内部是SA和 token, 外部用户进来就是 .kube/config文件 namespace下的是role&#xff0c;整个集群是 ClusterRole. 动作就是Binding li…

一文带你彻底搞懂什么是责任链模式!!

文章目录 什么是责任链模式&#xff1f;详细示例SpingMVC 中的责任链模式使用总结 什么是责任链模式&#xff1f; 在我们日常生活中&#xff0c;经常会出现一种场景&#xff1a;一个请求需要经过多个对象的处理才能得到最终的结果。比如&#xff0c;一个请假申请&#xff0c;需…

集训 Day 2 模拟赛总结

复盘 7&#xff1a;30 开题 想到几天前被普及组难度模拟赛支配的恐惧&#xff0c;下意识觉得题目很难 先看 T1&#xff0c;好像不是很难&#xff0c;魔改 Kruskal 应该就行 看 T2 &#xff0c;感觉很神奇&#xff0c;看到多串匹配想到 AC 自动机&#xff0c;又想了想 NOIP …

【开源】基于RMBG的一键抠图与证件照制作系统【含一键启动包】

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…