【C++】适配器· 优先级队列 仿函数 反向迭代器

news2024/11/17 7:49:31

目录

  • 适配器:
  • 适配器的应用:
    • 1. 优先级队列:
      • 仿函数:
        • 更深入的了解仿函数:
        • 一个关于不容易被注意的知识点:
    • 2. 反向迭代器:(list为例)

适配器:

我们先来谈来一下容器适配器的概念:

适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该种模式是将一个类的接口转换成客户希望的另外一个接口。

在这里插入图片描述

适配器的应用:

1. 优先级队列:

仿函数我们暂时不提。
优先级队列其实就是我们的常说的堆。

那我们直接按照堆的方式进行创建一个优先级队列的类即可。
可以参考堆的博客

另外由于我们是采取适配器模式,于是可以直接在模版列表多加一个vector<T>的缺省,
可以理解为对已有的容器进行封装形成我们需要的容器。

注意:我们此时的代码实现的是大堆

	template<class T, class Container = vector<T>>
	class priority_queue
	{
	public:
		priority_queue()
		{}

		template<class InputIterator>
		priority_queue(InputIterator first, InputIterator end)
			:con(first, end)
		{
			for (int i = (con.size() - 2) / 2; i >= 0; i--)
			{
				adjust_down(i);
			}
		}

		void adjust_up(int child)
		{
			while (child > 0)
			{
				int parent = (child - 1) / 2;
				if (con[parent] < con[child])
				{
					swap(con[parent], con[child]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}

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

		void adjust_down(int parent)
		{
			int child = parent * 2 + 1;
			while (child < con.size())
			{
				if (child + 1 < con.size() && con[child]< con[child + 1])
				{
					child++;
				}
				if (con[parent]< con[child])
				{
					swap(con[parent], con[child]);
				}
				parent = child;
				child = child * 2 + 1;
			}
		}

		void pop()
		{
			swap(con[0], con[con.size() - 1]);
			con.pop_back();
			adjust_down(0);
		}

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

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

		bool empty()
		{
			return con.size() == 0;
		}
	private:
		Container con;
	};

在另外说一下,对于迭代器区间构造我们选择使用向上调整算法,这是因为向上调整算法建堆是要优于向下建堆的
在这里插入图片描述

仿函数:

但是我们在这里还有一个问题,每次我们想改变大堆或者小堆时,都要进行对代码的修改,再C语言中我们可以使用函数指针来解决,qsort就是一个很好的例子。
在这里插入图片描述
使用函数指针进行升序还是降序的选择,我们的C++当然也可以选择这样做,但是C++并不喜欢指针,于是应时而生一个仿函数

什么是仿函数?
在这里插入图片描述
通过上图我们就可以得出一个结论:
当类中重载()操作符时即可称之为仿函数。

那我们现在就可以使用仿函数随心所欲的更改大堆还是小堆了

	template<class T>
	struct Less
	{
		bool operator()(const T& a, const T& b)
		{
			return a < b;
		}
	};
	template<class T>
	struct Greater
	{
		bool operator()(const T& a, const T& b)
		{
			return a > b;
		}
	};
	template<class T, class Container = vector<T>, class Compare = Less<T>>
	class priority_queue
	{
	public:
		priority_queue()
		{}

		template<class InputIterator>
		priority_queue(InputIterator first, InputIterator end)
			:con(first, end)
		{
			for (int i = (con.size() - 2) / 2; i >= 0; i--)
			{
				adjust_down(i);
			}
		}

		void adjust_up(int child)
		{
			
			while (child > 0)
			{
				Compare com;
				int parent = (child - 1) / 2;
				if (com(con[parent], con[child]))
				{
					swap(con[parent], con[child]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}

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

		void adjust_down(int parent)
		{
			int child = parent * 2 + 1;
			Compare com;
			while (child < con.size())
			{
				if (child + 1 < con.size() && com(con[child], con[child + 1]))
				{
					child++;
				}
				if (com(con[parent], con[child]))
				{
					swap(con[parent], con[child]);
				}
				parent = child;
				child = child * 2 + 1;
			}
		}

		void pop()
		{
			swap(con[0], con[con.size() - 1]);
			con.pop_back();
			adjust_down(0);
		}

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

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

		bool empty()
		{
			return con.size() == 0;
		}
	private:
		Container con;
	};

到这也就实现了与库中类似的优先级队列。

库中less实现的是小堆greater是大堆,我们这的实现与库中保持一致。

更深入的了解仿函数:

我们已经掌握了仿函数的初阶用法

假设我们现在有一个Date类

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	bool operator<(const Date& d)const
	{
		return (_year < d._year) ||
			(_year == d._year && _month < d._month) ||
			(_year == d._year && _month == d._month && _day < d._day);
	}
	bool operator>(const Date& d)const
	{
		return (_year > d._year) ||
			(_year == d._year && _month > d._month) ||
			(_year == d._year && _month == d._month && _day > d._day);
	}
	friend ostream& operator<<(ostream& _cout, const Date& d)
	{
		_cout << d._year << "-" << d._month << "-" << d._day;
		return _cout;
	}
private:
	int _year;
	int _month;
	int _day;
};

我们使用优先级队列对3个日期对象进行排序:

int main()
{
	cyc::priority_queue<Date> pq;
	Date d1(2000, 2, 20);
	pq.push(d1);
	pq.push(Date(2000, 2, 21));
	pq.push({2000, 2, 22});
	
	while (!pq.empty())
	{
		cout << pq.top() << endl;
		pq.pop();
	}
	cout << endl;

	return 0;
}

我们在这里使用了3中传参方式,有名对象,匿名对象,初始化列表传参。


一个关于不容易被注意的知识点:

匿名对象其实是具有const属性哦!

class A
{
private:
	int a;
	int b;
};

int main()
{
	const A& ref = A();
	//匿名对象是const对象。
	return 0;
}

不加const会报错。

但是我们const自定义类型对象也可以调用非const成员函数

class A
{
public:
	void print()
	{}
private:
	int a;
	int b;
};

int main()
{
	const A& ref = A();
	//匿名对象是const对象。
	A().print();
	return 0;
}

这是经常不被注意的一个点,也算是编译器对于某些场景的特殊处理。


不过到现在这也仅仅是我们已经掌握的知识,如果我们传入的是Date类型的指针呢?
在这里插入图片描述
答案依旧如我们所料,传入指针,那么指针是内置类型,肯定就直接对只怎的大小进行比较,那我们如何解决这个问题?

操作符重载可以吗?
答案是不可以的,不可重载内置类型,故排除此方案。

那么仿函数?
答案是肯定可以的。
如何使用仿函数进行解决?

struct PDateLess
{
	bool operator()(const Date* left, const Date* right)
	{
		return *left < *right;
	}
};

给模版类型时我们给自己实现的仿函数即可!

	cyc::priority_queue<Date*, vector<Date*>, PDateLess> pq;

	Date* pd1 = new Date(2000, 2, 20);
	Date* pd2 = new Date(2000, 2, 21);
	Date* pd3 = new Date(2000, 2, 22);
	pq.push(pd1);
	pq.push(pd2);
	pq.push(pd3);
	while (!pq.empty())
	{
		cout << *pq.top() << endl;
		pq.pop();
	}

所以,我们可以自定义仿函数行为!

到这里不知道有没有细心的小伙伴发现,我们有时候模版给的是类型,有时给的是对象

就像优先级队列与sort库函数。
本质的区别是因为一个是类模版,一个是函数模版!
在这里插入图片描述

2. 反向迭代器:(list为例)

我们在来进阶的看一下适配器,
适配器是将一个容器封装成我们需要的容器

stl库实现反向迭代器的思路是不是在重新写一个反向迭代器类,而是利用普通迭代器封装为反向迭代器!
他们之间是一个上下层的关系!
而const迭代器与普通迭代器是一个平行的关系!

首先如果我们自己利用如上思路进行实现的话。

大概率如下图代码一样,这样写的话,注意我们的重载*的返回值应该怎么写?

template<class Iterator>
struct ReverseIterator
{
	typedef ReverseIterator<Iterator> self;
	Iterator rit;

	ReverseIterator(Iterator it)
	{
		rit(it);
	}

	self& operator++()
	{
		rit--;
		return *this;
	}

	self& operator--()
	{
		rit++;
		return *this;
	}
	//返回值怎么写?
	operator*()
	{
		return *rit;
	}
};

为了解决这个问题我们可以再引入模版参数,与实现const时的解决方案相似。

#pragma once


template<class Iterator, class Ref, class Ptr>
struct ReverseIterator
{
	typedef ReverseIterator<Iterator, Ref, Ptr> self;
	Iterator rit;

	ReverseIterator(Iterator it)
	{
		rit(it);
	}

	self& operator++()
	{
		rit--;
		return *this;
	}

	self& operator--()
	{
		rit++;
		return *this;
	}

	Ref operator*()
	{
		return *rit;
	}

	Ptr operator->()
	{
		return rit.operator->();
	}
	
	bool operator!=(self it)
	{
		return rit != it.rit;
	}
};

再者,库中rbegin与rend是与我们begin与end的位置是直接翻转的在这里插入图片描述
讲究的就是一个对称。
那么这样进行打印的话就会打印出未知数。
我们期望的是4 3 2 1
在这里插入图片描述
解决方法也很简单,对*重载进行一下修改即可。
在这里插入图片描述
本篇文章就到此结束了,欢迎询问。

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

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

相关文章

CentOS 7安装、卸载MySQL数据库(一)

说明&#xff1a;本文介绍如何在CentOS 7操作系统下使用yum方式安装MySQL数据库&#xff0c;及卸载&#xff1b; 安装 Step1&#xff1a;卸载mariadb 敲下面的命令&#xff0c;查看系统mariadb软件包 rpm -qa|grep mariadb跳出mariadb软件包信息后&#xff0c;敲下面的命令…

OpenStack云计算(十)——OpenStack虚拟机实例管理,增加一个计算节点并进行实例冷迁移,增加一个计算节点的步骤,实例冷迁移的操作方法

项目实训一 本实训任务对实验环境要求较高&#xff0c;而且过程比较复杂&#xff0c;涉及的步骤非常多&#xff0c;有一定难度&#xff0c;可根据需要选做。可以考虑改为直接观看相关的微课视频 【实训题目】 增加一个计算节点并进行实例冷迁移 【实训目的】 熟悉增加一个…

力扣110. 平衡二叉树

思路&#xff1a;与二叉树最大高度类似&#xff0c;但是这里需要返回 -1 的高度来标识不是平衡二叉树&#xff0c;判断左右子树的高度相差大于1则不平衡&#xff0c;否则就是平衡。 class Solution {public boolean isBalanced(TreeNode root) {int ans func(root);if(ans >…

向Linux内核添加新功能的静态加载与动态加载

目录 向Linux内核添加新功能的静态加载与动态加载 开发环境&#xff1a; 一、静态加载法 1、新功能源码与Linux内核源码在同一个目录结构下 2、给新功能代码配置Kconfig 3、给新功能代码改写Makefile 4、使用make menuconfig 在配置界面里将新功能对应的那项选择成<*&…

AI商业智能的一些分享

本文主要讲AI商业相关的&#xff08;特别是营销相关的&#xff09;一些知识点&#xff0c;比较零散。 简单总结 AI商业智能&#xff1a; 1&#xff09;将人员经验抽象化为算法规则&#xff0c; 2)打造数据驱动的精益运营能力&#xff0c; 3)长期保持价格竞争力并将商品毛利让…

Java中的重写

package day34; ​ public class Father {String name;int age;public void 输出(){System.out.println("father");} } ​ package day34; ​ public class Son extends Father{Overridepublic void 输出() {System.out.println("son");} } ​ package d…

【LAMMPS学习】八、基础知识(3.8)计算扩散系数

8. 基础知识 此部分描述了如何使用 LAMMPS 为用户和开发人员执行各种任务。术语表页面还列出了 MD 术语&#xff0c;以及相应 LAMMPS 手册页的链接。 LAMMPS 源代码分发的 examples 目录中包含的示例输入脚本以及示例脚本页面上突出显示的示例输入脚本还展示了如何设置和运行各…

Spring基础 SpringAOP

前言 我们都知道Spring中最经典的两个功能就是IOC和AOP 我们之前也谈过SpringIOC的思想 容器编程思想了 今天我们来谈谈SpringAOP的思想 首先AOP被称之为面向切面编程 实际上面向切面编程是面向对象的编程的补充和完善 重点就是对某一类问题的集中处理 前面我们写的统一异常管理…

2024接口自动化测试入门基础知识【建议收藏】

接口自动化测试是指通过编写测试脚本和使用相关工具&#xff0c;对软件系统的接口进行自动化测试的过程。 今天本文从4个方面来介绍接口自动化测试入门基础知识 一、接口自动化测试是什么&#xff1f; 二、接口自动化测试流程&#xff1f; 三、接口自动化测试核心知识点有那些…

Linux--uboot移植(二)

修改uboot以匹配开发板的方式有两种&#xff0c;一种是在NXP原厂开发板i.MX 6ULL EVK的文件上进行修改&#xff0c;另一种仿造NXP的开发板文件&#xff0c;添加自己的开发板文件。 为了能更多的了解uboot&#xff0c;我们使用代码改动较大的第二种方式进行uboot的移植。 在修…

HTML5+JavaScript实现语音合成(文字转语音)

HTML5JavaScript实现语音合成&#xff08;文字转语音&#xff09; 本文介绍用HTML5和JavaScript实现语音合成朗读&#xff08;文字转语音&#xff09;。 Web Speech API 有两个部分&#xff1a;SpeechSynthesis 语音合成&#xff08;文本到语音 TTS&#xff09;和 SpeechReco…

双周回顾#009 - 大黄和我

双周回顾#009 - 大黄和我 说一个笑话&#xff0c;被朋友催更了&#xff0c;这就有点尴尬了&#xff01;&#xff01;&#xff01; 最近一个月&#xff0c;把空闲时间都点在了“玩”上&#xff0c;看闲书、玩游戏、背包客。 部落冲突 《埃隆马斯克传》一书中&#xff0c;马斯…

ubunt18.04安装ROS避坑指南

1 前言&#xff1a; 本文是ubunt18.04下安装ros的&#xff0c;如果想使用ros2,请看我的下一篇文章,ubunt18.04安装ROS2 本来是不该写的&#xff0c;有官方文档&#xff0c;还有很多大神的分析。但里面的坑太多&#xff0c;我考虑了一下&#xff0c;还是想记录下避免下次在跳泥…

day06 51单片机-点阵led

1 点阵LED 1.1 需求描述 本案例介绍如何使用点阵LED显示一排由左上到右下的斜线。 1.2 硬件设计 1.2.1 硬件原理图 点阵内部的原理图: 点阵LED的原理也非常简单,就是LED点灯。例如,我们想要让13列(阳极端)9行(阴极端)的LED点亮,需要13为高电平,9为低电平。注意对于…

《王者荣耀》游戏攻略:角色排行榜——墨子

作为天蝎座黄金圣斗士&#xff0c;墨子以他的正义感和荣誉感闻名&#xff0c;一直站在正义的一方&#xff0c;忠于女神雅典娜。他的猩红毒针象征着审判和死亡&#xff0c;而他所施加的十五针“安达里士”更是生命终结的预兆。在冥王再次发动战争之际&#xff0c;墨子追随雅典娜…

ResNet详解

一、认识ResNet ResNet&#xff08;Residual Network&#xff09;是一种深度神经网络结构&#xff0c;被广泛应用于图像分类、目标检测和语义分割等计算机视觉任务中。它是由微软亚洲研究院的何凯明等人于2015年提出的&#xff0c;通过引入残差连接&#xff08;residual conne…

javaScript3

javaScript 一.对象1.概念2.创建对象的三种方法(1).字面量创建&#xff08;利用{}&#xff09;(2)变量、属性、函数、方法的区别(3).new Object创建(4).构造函数 3.new关键字的执行过程4.遍历对象&#xff08;for..in) 二.内置对象 一.对象 1.概念 一组无序的相关属性和方法的…

Pycharm破解流程

1.下载pycharm 网上很多&#xff0c;随便找一个&#xff0c;懒得找的话&#xff0c;或者去我传上去的资源pycharm部分直接取 2.下载文件 文件部分&#xff0c;我放在pycharm文件里面一起 打开下载好的激活包 3.执行脚本 先执行unisntall-all-users.vbs,直接双击打开&#xff0c…

咱们来唠唠2024年参加三下乡社会实践活动怎样联系媒体投稿?

作为一名在校大学生,我深深珍视每一次增长见识、锻炼能力的机会,每年寒暑假期间,我都积极参与学院的“三下乡”社会实践活动。这不仅是我贴近生活、体验基层的大好时机,更是我积累人生经验、拓宽视野的重要途径。每次活动结束后,按照学院的要求,我们需要将实践纪实整理成文,向各…

《神奇女侠3:暗黑之魂》AI制作电影短片(下)

《神奇女侠3&#xff1a;暗黑之魂》AI制作电影短片&#xff08;下&#xff09; 黑暗滋生&#xff0c;世界沦陷&#xff0c;神奇女侠独战群魔&#xff0c;唤醒挚爱&#xff0c;守护最后的光明&#xff01; 《神奇女侠3&#xff1a;暗黑之魂》&#xff08;下&#xff09;故事继续…