【C++】反向迭代器仿函数模板进阶

反向迭代器&仿函数&模板进阶

  • 一,反向迭代器
    • 1. 什么是反向迭代器
    • 2. 模拟实现
    • 3. 如何使用
  • 二,仿函数
    • 1. 仿函数的概念
    • 2. 仿函数的用法
  • 三,模板
    • 1. 非类型模板参数
    • 2. 模板的特化
      • 2.1 特化概念
      • 2.2 函数模板特化
      • 2.3 类模板特化
        • 2.3.1 全特化
        • 2.3.2 偏特化
    • 3. 模板分离编译

一,反向迭代器

1. 什么是反向迭代器

在前面的讲解中我们可以利用迭代器来遍历容器,迭代器有正向迭代器和反向迭代器,在前面的模拟实现中我们都实现是正向迭代器,在这里我们来模拟实现一下反向迭代器。

2. 模拟实现

上一讲我们知道了适配器的概念,其可以将底层的容器封装成自己所需的容器。这里反向迭代器的实现也可以用适配器的方式实现。

具体来说就是将反向迭代器写成一种泛型的模板类,通过传入不同容器的正向迭代器来适配出反向迭代器。

template<class Iterator,class Ref,class Ptr>
struct Reverse_iterator {typedef Reverse_iterator<Iterator, Ref, Ptr> Self;//Ref-->const T&,Ptr-->const T*Iterator _cur;//...
}

对于其他的重载operator--或者operator++我们也只需要稍作修改即可

template<class Iterator,class Ref,class Ptr>
struct Reverse_iterator {typedef Reverse_iterator<Iterator, Ref, Ptr> Self;//Ref-->const T&,Ptr-->const T*Iterator _cur;Reverse_iterator(Iterator lt) :_cur(lt){}//前置++Self& operator++() {--_cur;return *this;}//后置++Self& operator++(int) {Self tmp = _cur;--_cur;return *this;}//前置--Self& operator--() {++_cur;return *this;}//后置--Self& operator--(int) {Self tmp = _cur;++_cur;return *this;}Ref operator*() {Iterator tmp = _cur;--tmp;//这里--tmp是为了和正向迭代器对称return *tmp;}Ptr operator->() {return &(operator*());}bool operator!=(const Self& s) {return _cur != s._cur;}bool operator==(const Self& s) {return _cur == s._cur;}
};

3. 如何使用

以我们先前模拟实现的list为例,我们在list的类中声明反向迭代器时在对应的模板参数传入list的正向迭代器,再实现相应的反向迭代器的相关操作就可以了。

template<class T>
class mylist {typedef ListNode<T> Node;//方便阅读public:typedef List_Iterator<T,T&,T*> iterator;//写成公有,类外也可以访问typedef List_Iterator<T,const T&,const T*> const_iterator;typedef Reverse_iterator<iterator, T&, T*> reverse_iterator;typedef Reverse_iterator<const_iterator,const T&,const T*> const_reverse_iterator;

注:根据上面写的反向迭代器的适配器模板参数,传入的是list的迭代器,如果要实现其他容器的反向迭代器就传入相应的迭代器。


同时,根据正向迭代器来实现反向迭代器的相应的接口

reverse_iterator rbegin() {return reverse_iterator(end());
}reverse_iterator rend() {return reverse_iterator(begin());
}

二,仿函数

1. 仿函数的概念

仿函数其实就是一个可以像函数一样调用的类,这个类需要重载()
在上一讲中我们知道优先队列默认大堆的原因是其默认的仿函数是less,按升序存放,与之对应的是greater,如果传入的是greater则优先队列会是小堆,按降序存放。

下面我们借助模拟实现优先队列来具体讲解仿函数。

2. 仿函数的用法

在上一讲我们直接写了代码来控制向上调整和向下调整是建的大堆还是小堆,这里我们用仿函数来实现

我们先写出这两个仿函数的类,并重载()

template<class T>
class Less {
public:bool operator()(const T& x,const T& y) {return	x < y;}
};template<class T>
class Greater {
public:bool operator()(const T& x, const T& y) {return x > y;}
};

然后我们将其嵌入到建堆算法中,先定义出这个类的对象,然后在判断时调用这个类,可以看到下面代码中com(_con[parent],_con[child])这句就是调用了仿函数,像函数一样调用的类,

//向上调整
void Adjust_up(int child) {Compare com;int parent = (child - 1) / 2;while (child > 0) {//if(_con[child] > _con[parent])if (com(_con[parent],_con[child]))//默认大堆 {					swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else {break;}}
}void Adjust_down(size_t parent) {Compare com;size_t child = parent * 2 + 1;while (child < _con.size()) {if (child + 1 < _con.size() && com(_con[child],_con[child + 1])) {++child;//找到大的那个孩子节点}//if(_con[parent] < _con[child])if (com(_con[parent],_con[child])) {swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else {break;}}
}

三,模板

前面我们讲过模板的基本用法,现在我们来进一步学习一下模板

1. 非类型模板参数

模板参数分类类型形参与非类型形参

类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。

非类型形参:就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用

看下面的代码:这里的 N 就是非类型模板参数

// 定义一个模板类型的静态数组
template<class T, size_t N = 10>
class array
{
public:T& operator[](size_t index){return _array[index];}const T& operator[](size_t index)const{return _array[index];}size_t size()const{return _size;}bool empty()const{return 0 == _size;}
private:T _array[N];size_t _size;
}

注意:

  1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的。
  2. 非类型的模板参数必须在编译期就能确认结果

2. 模板的特化

2.1 特化概念

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结
果,需要特殊处理,比如:实现了一个专门用来进行小于比较的函数模板

举个例子,看下面的代码:

template<class T>
bool Less(T left, T right)
{return left < right;
}class Date{
public:Date(int year,int month,int day):_year(year),_month(month),_day(day){}//....
private:int _year;int _month;int _day;
};int main()
{cout << Less(1, 2) << endl; // 可以比较,结果正确Date d1(2022, 7, 7);Date d2(2022, 7, 8);cout << Less(d1, d2) << endl; // 可以比较,结果正确Date* p1 = &d1;Date* p2 = &d2;cout << Less(p1, p2) << endl; // 可以比较,结果错误return 0;
}

根据以上的代码可以看出Less绝对多数情况下都可以正常比较,但是在特殊场景下就得到错误的结果。上述示例中,p1指向的d1显然小于p2指向的d2对象,但是Less内部并没有比较p1和p2指向的对象内容,而比较的是p1和p2指针的地址,这就无法达到预期而错误。

此时,就需要对模板进行特化。即在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。


模板特化分为函数模板特化和类模板特化。

2.2 函数模板特化

函数模板的步骤是:

  1. 必须要先有一个基础的函数模板
  2. 关键字template后面接一对空的尖括号<>
  3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
  4. 函数形参表必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误

以上面的例子来说,如果想要比较Date的日期,可以堆Less进行特化,调用时会走特化的版本:

template<>
bool Less<Date*>(Date* left, Date* right)
{return *left < *right;
}class Date{
public:Date(int year,int month,int day):_year(year),_month(month),_day(day){}//....
private:int _year;int _month;int _day;
};int main()
{cout << Less(1, 2) << endl; // 可以比较,结果正确Date d1(2022, 7, 7);Date d2(2022, 7, 8);cout << Less(d1, d2) << endl; // 可以比较,结果正确Date* p1 = &d1;Date* p2 = &d2;cout << Less(p1, p2) << endl; // 可以比较,结果正确return 0;
}

但是,有一种便捷的方法,那就是直接重载一个。
一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出

如下:

bool Less(Date* left, Date* right)
{return *left < *right;
}

该种实现简单明了,代码的可读性高,容易书写,因为对于一些参数类型复杂的函数模板,特化时特别给
出,因此函数模板不建议特化。

2.3 类模板特化

特化又分为全特化偏特化

2.3.1 全特化

全特化就是将模板参数全部确定

template<>
class Date<int, char>{
public:Date(){}//....
private:int _a1;char _a2;
};
2.3.2 偏特化

偏特化就是针对模版参数进一步进行条件限制设计的特化版本。

偏特化有两种表现形式:

部分特化:将模板参数的一部分特化

template <class T1>
class Data<T1, int>
{
public:Data() {cout<<"Data<T1, int>" <<endl;}
private:T1 _d1;int _d2;
};

这个时候,当实例化对象时只要传入的第二个模板参数是int就会进行部分特化


参数更进一步的限制:偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本

//两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public:Data() {cout<<"Data<T1*, T2*>" <<endl;}
private:T1 _d1;T2 _d2;
};
//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:Data(const T1& d1, const T2& d2): _d1(d1), _d2(d2){cout<<"Data<T1&, T2&>" <<endl;}	
private:const T1 & _d1;const T2 & _d2;
};
void test2 ()
{Data<double , int> d1; // 调用特化的int版本Data<int , double> d2; // 调用基础的模板Data<int *, int*> d3; // 调用特化的指针版本Data<int&, int&> d4(1, 2); // 调用特化的指针版本
}

3. 模板分离编译

一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式

在这里不建议将模板的声明和定义分离,直接写在一个头文件就可以。


至此我们终于将C++中STL容器讲解完并且模拟实现了只要的接口,初步感受了C++面向对象编程的优点,后面我们会带来更加深入的C++的知识,请各位看官持续关注。
在这里插入图片描述

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

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

相关文章

Flink 性能优化总结(内存配置篇)

内存配置优化 Flink 内存模型 内存模型详解 进程内存&#xff08;Total Process Memory&#xff09;&#xff1a;Flink 进程内存分为堆上内存和堆外内存&#xff0c;堆上内存和 堆外内存的主要区别在于它们的管理方式不同和使用方式不同&#xff0c;这些会影响到它们的性能和…

矩阵最大权值

题目描述 现有一个n∗m大小的矩阵&#xff0c;矩阵中的每个元素表示该位置的权值。现需要从矩阵左上角出发到达右下角&#xff0c;每次移动只能向上下左右移动一格&#xff08;不允许移动到曾经经过的位置&#xff09;。求最后到达右下角时路径上所有位置的权值之和的最大值。…

python实现--折半查找

python实现–顺序查找 python实现–折半查找 python实现–分块查找 python实现B/B树 折半查找&#xff08;Binary Search&#xff09;&#xff0c;也称为二分查找&#xff0c;是一种高效的查找算法&#xff0c;适用于有序数组或列表。它的基本思想是通过每次将查找范围缩小为原…

vue 渲染pdf并盖章之后生成新的pdf

目录 1.渲染pdf 1.页面准备渲染的容器,使用canvas渲染 2.把文件流渲染到canvas上面 3.下载pdf.js插件 4.使用 2.盖章 1.印章图片 2.自定义指令拖拽 3.生成新的pdf 1.下载生成pdf插件 2.使用 4.页面效果图 1.展示pdf 2.拖拽盖章 3.生成pdf文件 5.整体代码 …

蓝月亮,蓝禾,奇安信,三七互娱,顺丰,康冠科技,金证科技24春招内推

蓝月亮&#xff0c;蓝禾&#xff0c;奇安信&#xff0c;三七互娱&#xff0c;顺丰&#xff0c;康冠科技&#xff0c;金证科技24春招内推 ①蓝月亮 【招聘岗位】技术&#xff0c;研发&#xff0c;lT&#xff0c;供应链&#xff0c;市场&#xff0c;职能等 【网申链接】https://s…

机器学习是什么?

机器学习是一门多学科交叉专业&#xff0c;涵盖概率论知识、统计学知识、近似理论知识和复杂算法知识等。它是人工智能领域的关键技术&#xff0c;专门研究计算机如何模拟或实现人类的学习行为&#xff0c;以获取新的知识或技能&#xff0c;并重新组织已有的知识结构&#xff0…

中兴R5300G4无法识别全部硬盘与服务器Smart31002100RAID卡修改端口模式配置方法

中兴R5300G4无法识别全部硬盘&#xff0c;需要启动UEFI模式。 问题描述 硬盘配置RAID或者HBA直通模式需要修改RAID卡的端口模式。 本文介绍服务器分别在legacy、UEFI模式下的配置方法。 适用产品 R5300 G4、R5500 G4、R8500 G4 解决方案 一&#xff0e;Legacy启动模式&#x…

编程笔记 html5cssjs 009 HTML链接 我的网址簿

编程笔记 html5&css&js 009 HTML链接 我的网址簿 一、代码二、解释 这段代码的主要功能是展示一个包含多个分类和网址的网址簿&#xff0c;每个分类下有多个网址链接。通过a标签的href属性&#xff0c;用户可以点击链接跳转到相应的网址。同时&#xff0c;通过style标签…

《剑指 Offer》专项突破版 - 面试题 77 和 78 : 详解归并排序(C++ 实现)

目录 归并排序详解 递归实现 迭代实现 面试题 77 : 链表排序 面试题 78 : 合并排序链表 法一、利用最小堆选取值最小的节点 法二、按照归并排序的思路合并链表 归并排序详解 归并排序就是将两个或两个以上的有序表合并成一个有序表的过程。将两个有序表合并成一个有序表…

机器学习-04-分类算法-01决策树案例

总结 本系列是机器学习课程的系列课程&#xff0c;主要介绍机器学习中分类算法&#xff0c;本篇为分类算法开篇与决策树部分。 本门课程的目标 完成一个特定行业的算法应用全过程&#xff1a; 懂业务会选择合适的算法数据处理算法训练算法调优算法融合 算法评估持续调优工程…

redis 缓冲区详解(性能优化缓冲区优化)

目录 前言 客户端输入缓冲区 输出缓冲区 集群缓冲区 全量复制缓冲区问题 增量复制缓冲区问题 前言 在我的《Redis 为啥那么快》这篇文章中&#xff0c;详细总结了Redis 为啥那么快。今天当我要详细阐述Redis 的缓冲区时&#xff0c;意识到应该加上Redis 的缓冲区。我们假…

初级爬虫实战——巴黎圣母院新闻

文章目录 发现宝藏一、 目标二、简单分析网页1. 寻找所有新闻2. 分析模块、版面和文章三、爬取新闻1. 爬取模块2. 爬取版面3. 爬取文章四、完整代码五、效果展示发现宝藏 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。【宝藏入口】。 …

python-0001-安装虚拟环境

版本 软件版本python3.6django2.2.5sqlite33.45.1pycharm2023.3.4 升级sqlite3 下载地址&#xff1a;https://download.csdn.net/download/qq_41833259/88944701 升级命令&#xff1a; tar -zxvf sqlite-autoconf-3399999.tar.gz cd sqlite-autoconf-3399999 ./configure m…

01_04_JavaWEB03_XML、Tomcat、http

XML_Tomcat10_HTTP 参考尚硅谷再总结复习 一 XML XML是EXtensible Markup Language的缩写&#xff0c;翻译过来就是可扩展标记语言。所以很明显&#xff0c;XML和HTML一样都是标记语言&#xff0c;也就是说它们的基本语法都是标签。 可扩展 三个字表面上的意思是XML允许自定义…

docker套娃实践(待续)

安装Docker sudo apt-get updatesudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-pluginsudo usermod -aG docker $USERsudo chmod 666 /var/run/docker.sockDocker基础命令 # 基础命令 启动docker&#xff1a; systemctl start docker 停止dock…

VBA combox/listbox 控件响应鼠标滚轮事件

在vba中&#xff0c;我们在用户窗体中如果添加有combox控件&#xff0c;或者是listbox控件。正常情况下&#xff0c;combox 和 listbox 是不响应鼠标滚轮事件的&#xff0c;且默认的VBA控件中&#xff0c;也没有提供响应鼠标滚轮事件的方法和入口。如此以来&#xff0c;我们在c…

【毕设级项目】基于AI技术的多功能消防机器人(完整工程资料源码)

基于AI技术的多功能消防机器人演示效果 竞赛-基于AI技术的多功能消防机器人视频演示 前言 随着“自动化、智能化”成为数字时代发展的关键词&#xff0c;机器人逐步成为社会经济发展的重要主体之一&#xff0c;“机器换人”成为发展的全新趋势和时代潮流。在可预见的将来&#…

1. ag=do,act做,动

记忆单词非难事&#xff0c;原有捷径&#xff0c;认得ag识词多&#xff0c;立竿见影。 agdo&#xff0c;act做&#xff0c;动 agent&#xff3b;eiʤ(ə)nt&#xff3d;n.【ag 做&#xff0c;办理&#xff0c;-ent名词后缀&#xff0c;表示人&#xff1b;“做事者”&#xff…

Adobe Photoshop 2024 v25.5.1 for mac 强大的图形编辑工具 兼容 M1/M2/M3

Mac毒搜集到的Adobe Photoshop 2024 v25.5.1 是一款强大的图形编辑和设计工具! v25.5.1版本AI生成式无法使用 应用介绍 Adobe Photoshop 2024是一款强大的图像处理软件&#xff0c;由Adobe公司开发。它可以用于编辑和处理照片、图形和其他类型的图像&#xff0c;包括设计、绘画…

Mermaid 流程图

​ 文章目录 obsidianmermaid其它mermaid 使用flowchartobsidian 下载 obsidian obsidian apk下载办法_obsidian安卓版下载-CSDN博客 mermaid GitHub - mermaid-js/mermaid: Generation of diagrams like flowcharts or sequence diagrams from text in a similar manner a…