【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.hqwc.cn/news/625259.html

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

相关文章

设计模式——2_9 模版方法(Template Method)

人们往往把任性也叫做自由&#xff0c;但是任性只是非理性的自由&#xff0c;人性的选择和自决都不是出于意志的理性&#xff0c;而是出于偶然的动机以及这种动机对感性外在世界的依赖 ——黑格尔 文章目录 定义图纸一个例子&#xff1a;从文件中获取信息分几步&#xff1f;Rea…

Adobe AE(After Effects)2017下载地址及安装教程

Adobe After Effects是一款专业级别的视觉效果和动态图形处理软件&#xff0c;由Adobe Systems开发。它被广泛用于电影、电视节目、广告和其他多媒体项目的制作。 After Effects提供了强大的合成和特效功能&#xff0c;可以让用户创建出令人惊艳的动态图形和视觉效果。用户可以…

MapReduce 机理

1.hadoop 平台进程 Namenode进程: 管理者文件系统的Namespace。它维护着文件系统树(filesystem tree)以及文件树中所有的文件和文件夹的元数据(metadata)。管理这些信息的文件有两个&#xff0c;分别是Namespace 镜像文件(Namespace image)和操作日志文件(edit log)&#xff…

vscode编译c++报错解决方案

1&#xff0c;xxxx cl.exe 一大串什么非程序员的(应该是这些&#xff09;,就是看一些谁的&#xff0c;调用了Visual Studio的编译软件去运行。建议&#xff0c;不要这样搞。 解决方案1&#xff1a;每次用就看这个文章&#xff08;个人觉得很麻烦&#xff09;&#xff1a;仅当…

java正则表达式教程

什么是正则表达式&#xff1a; 正则表达式是一种用来描述字符串模式的语法。在 Java 中&#xff0c;正则表达式通常是一个字符串&#xff0c;它由普通字符&#xff08;例如字母、数字、标点符号等&#xff09;和特殊字符&#xff08;称为元字符&#xff09;组成。这些特殊字符可…

NLP基础—jieba分词

jieba分词 支持四种分词模式 精确模式 试图将句子最精确地切开,适合文本分析;全模式 把句子中所有的可以成词的词语都扫描出来, 速度非常快,但是不能解决歧义;搜索引擎模式 在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词。paddle模式 利用Paddle…

WARNING: No swap limit support——查看docker状态时提示警告

环境&#xff1a;Ubuntu 20.04 1、警告详情 执行命令 service docker status如下图 2、解决办法 2.1 修改文件 执行命令 vim /etc/default/grub在GRUB_CMDLINE_LINUX中追加cgroup_enablememory swapaccount1&#xff0c;如下&#xff1a; # If you change this file…

elmentui树形表格使用Sortable拖拽展开行时拖拽bug

1、使用elemntui的el-table使用Sortable进行拖拽&#xff0c;如下 const el this.$el.querySelector(.el-table__body-wrapper tbody) Sortable.create(el, {onEnd: (event) > {const { oldIndex, newIndex } event//拿到更新前后的下标即可完成数据的更新} })2、但是我这…

分析ARP解析过程

1、实验环境 主机A和主机B连接到交换机&#xff0c;并与一台路由器互连&#xff0c;如图7.17所示&#xff0c;路由器充当网关。 图7.17 实验案例一示意图 2、需求描述 查看 ARP 相关信息,熟悉在PC 和 Cisco 设备上的常用命令,设置主机A和主机B为同一个网段网关设置为路由接…

2024-14.python前端+Django

第四篇 web前端 第1章 、Web的基本概念 前端基础总共分为三部分&#xff1a;html、css和js。 1.3、HTTP协议 1.3.1 、http协议简介 HTTP协议是Hyper Text Transfer Protocol&#xff08;超文本传输协议&#xff09;的缩写,是用于万维网&#xff08;WWW:World Wide Web &am…

故障转移-redis

4.4.故障转移 集群初识状态是这样的&#xff1a; 其中7001、7002、7003都是master&#xff0c;我们计划让7002宕机。 4.4.1.自动故障转移 当集群中有一个master宕机会发生什么呢&#xff1f; 直接停止一个redis实例&#xff0c;例如7002&#xff1a; redis-cli -p 7002 sh…

LVM和磁盘配额

目录 1、LVM &#xff08;1&#xff09;LVM机制 &#xff08;2&#xff09;LVM的管理命令 &#xff08;3&#xff09;创建并使用LVM &#xff08;4&#xff09;扩容 2、磁盘配额 &#xff08;1&#xff09;什么叫磁盘配额 &#xff08;2&#xff09;磁盘配额的条件和特点…