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

目录

  • 适配器:
  • 适配器的应用:
    • 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 oncetemplate<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.mzph.cn/news/821933.shtml

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

相关文章

【网络编程】如何创建一个自己的并发服务器?

hello &#xff01;大家好呀&#xff01; 欢迎大家来到我的网络编程系列之如何创建一个自己的并发服务器&#xff0c;在这篇文章中&#xff0c;你将会学习到在Linux内核中如何创建一个自己的并发服务器&#xff0c;并且我会给出源码进行剖析&#xff0c;以及手绘UML图来帮助大家…

素描石膏像:写实与抽象的美学对话

正文&#xff1a; 素描&#xff0c;作为绘画艺术的基础&#xff0c;承载着艺术家对世界的独特理解和表达。在素描石膏像的创作中&#xff0c;写实与抽象两种风格的碰撞&#xff0c;不仅展现了艺术家的技巧&#xff0c;更是一场美学的对话。 一、写实风格&#xff1a;细节的捕…

基于HMM隐马尔可夫模型的金融数据预测算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于HMM隐马尔可夫模型的金融数据预测算法.程序实现HMM模型的训练&#xff0c;使用训练后的模型进行预测。 2.测试软件版本以及运行结果展示 MATLAB2022A版本运…

CentOS 7 文件权限管理详解

CentOS 7 文件权限管理详解 在 CentOS 7 系统中,文件权限管理是一项至关重要的任务,它确保了系统安全、数据完整性和用户隐私。本文将详细介绍 CentOS 7 中的文件权限管理,包括相关文件和命令的使用。 一、文件权限基础 在 Linux 系统中,每个文件和目录都有与之关联的权…

简单工厂模式抽象工厂模式

一.简单工厂模式 1.什么是工厂模式 工厂模式的核心就是把对象的生产过程交给工厂来完成&#xff0c;当外部需要一个对象时&#xff0c;由工厂提供接口来获取对象&#xff0c;这样一来即使生产对象的过程变了&#xff0c;仍然不影响外部对对象的获取和使用。 2.举个栗子 定义…

云计算笔记

RAID的组合方式 RAID0&#xff1a;多个硬盘同时工作&#xff0c;可提供性能&#xff0c;无冗余机制 RAID1&#xff1a;数据保存多份&#xff0c;提供冗余机制&#xff0c;性能受到影响 RAID3&#xff1a;存在数据盘和单独校验盘&#xff0c;数据写入 至数据盘后需要运算且将…

吃透2000-2024年600道真题和解析,科学高效通过2025年AMC8竞赛

为帮助孩子科学、有效备考AMC8竞赛&#xff0c;我整理了2000-2004年的全部AMC8真题&#xff08;完整版共600道&#xff0c;且修正了官方发布的原试卷中的少量bug&#xff09;&#xff0c;并且独家制作成多种在线练习&#xff0c;利用碎片化时间&#xff0c;8个多月的时间足以通…

Django中的实时通信:WebSockets与异步视图的结合【第167篇—实时通信】

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 在现代Web应用程序中&#xff0c;实时通信已经成为了必不可少的功能之一。无论是在线聊天、…

爆肝3k字!掌握Spring与Redis的高效交互:从Jedis到Spring Data Redis

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

甲辰年三月初九有感

甲辰年三月初九有感 雨多汇成洪&#xff0c;梅酸长几成&#xff1f; ​久旱微巨调&#xff0c;点积多少恒。 ​时光沙漏里&#xff0c;情境入己梦。 望山山外山&#xff0c;​登顶人为峰。

Spring Boot 多环境配置:YML 文件的三种高效方法

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

Linux内核之WRITE_ONCE用法实例(四十八)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

单链表实现通讯录-三万字

声明 这一篇文章我会从单链表的概念&#xff0c;单链表的原理&#xff0c;一直到通讯录项目单链表的实现&#xff0c;再把单链表的专用题型系统的讲解一下&#xff08;文章较长&#xff09;。同时建议学习单链表之前可以学习一下顺序表&#xff0c;作为知识铺垫顺序表&#xf…

FreeRTOS 4.16作业

1、总结keil5下载代码和编译代码需要注意的事项 1&#xff09;使用STM32Cubemx创建工程 2&#xff09;先build编译排除错误 3&#xff09;点击魔术棒&#xff0c;选择Debug选项&#xff0c;找到使用的仿真器&#xff0c;选择ST-LINK仿真器&#xff0c;点击settings&#xff…

62、ARM/STM32开发板按键中断相关学习20240416

实现开发板上三个按键按下后触发中断&#xff0c;控制LED灯的亮灭。 【本次实验现象为&#xff1a;按键1&#xff08;key1&#xff09;控制开灯&#xff0c;key3控制关灯&#xff0c;key2按下LED灯闪烁五次】 代码&#xff1a; 头文件mykey.h: #ifndef __MYKEY_H__ #define…

【Java】@RequestMapping注解在类上使用

RequestMapping 是 Spring Web 应用程序中最常被用到的注解之一。这个注解会将 HTTP 请求映射到控制器&#xff08;controller类&#xff09;的处理方法上。 Request Mapping 基础用法 在 Spring MVC 应用程序中&#xff0c;RequestDispatcher (在 Front Controller 之下) 这…

Google Guava第五讲:本地缓存实战及踩坑

本地缓存实战及踩坑 本文是Google Guava第五讲,先介绍为什么使用本地缓存;然后结合实际业务,讲解如何使用本地缓存、清理本地缓存,以及使用过程中踩过的坑。 文章目录 本地缓存实战及踩坑1、缓存系统概述2、缓存架构演变2.1、无缓存架构2.2、引入分布式缓存问题1:为什么选…

【HCIP学习】OSPF协议基础

一、OSPF基础 1、技术背景&#xff08;RIP中存在的问题&#xff09; RIP中存在最大跳数为15的限制&#xff0c;不能适应大规模组网 周期性发送全部路由信息&#xff0c;占用大量的带宽资源 路由收敛速度慢 以跳数作为度量值 存在路由环路可能性 每隔30秒更新 2、OSPF协议…

用ChatGPT轻松撰写出色论文

ChatGPT无限次数:点击直达 用ChatGPT轻松撰写出色论文 在当今信息爆炸的时代&#xff0c;写作是一项必不可少的技能。无论是学术论文、科技报道还是人文文学&#xff0c;写作都扮演着重要的角色。然而&#xff0c;写作并非每个人的强项&#xff0c;有时候我们会遇到写作灵感枯…

Python中的GIL(全局解释器锁):理解其对多线程编程的影响

Python中的GIL&#xff08;全局解释器锁&#xff09;&#xff1a;理解其对多线程编程的影响 在深入探讨Python编程的高级主题时&#xff0c;全局解释器锁&#xff08;GIL&#xff09;是一个不可忽视的概念。GIL是Python解释器中的一个互斥锁&#xff0c;它对多线程编程有着显著…