【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;这些会影响到它们的性能和…

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

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

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

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

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

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

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

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

01_04_JavaWEB03_XML、Tomcat、http

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

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

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

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

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

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;包括设计、绘画…

Java双非大二找实习记录

先说结论&#xff1a;2.22→3.6线上线下面了七家&#xff0c;最后oc两家小公司&#xff0c;接了其中一个。 本人bg&#xff1a; 真名不经传双非一本&#xff0c;无绩点无竞赛无奖项无实习&#xff0c;23年12月开始学java。若非要说一点相关的经历&#xff0c;就是有java基础&…

XWPFDocument中XmlCursor的使用

类名&#xff1a; org.apache.xmlbeans Interface XmlCursor版本&#xff1a; 原xml代码&#xff1a; <w:p w14:paraId"143E3662" w14:textId"4167FBA7" w:rsidR"001506F2" w:rsidRPr"003F3D89" w:rsidRDefault"001506F2&qu…

Python3虚拟环境之virtualenv

virtualenv 在开发Python应用程序的时候&#xff0c;系统安装的Python3只有一个版本&#xff1a;3.7。所有第三方的包都会被pip安装到Python3的site-packages目录下。 如果要同时开发多个应用程序&#xff0c;这些应用程序都会共用一个Python&#xff0c;就是安装在系统的Pyt…

【三】安装k8s+kuboard, 拉取harbor镜像并执行yml文件

自己的配置 我在尊云上两百多买了三台2c4g的服务器&#xff0c;其实买两台就够了。 修改服务网卡掩码 确保几台服务器内网之间可以ping通 以尊云为例&#xff0c;vi /etc/sysconfig/network-scripts/ifcfg-eth1 修NETMASK值为255.0.0.0&#xff0c;重启服务器&#xff0c;尝试…

HarmonyOS 关系型数据 整体测试 进行 初始化 增删查改 操作

好啊 前面的文章 HarmonyOS 数据持久化 关系型数据库之 初始化操作 HarmonyOS 数据持久化 关系型数据库之 增删改逻辑编写 HarmonyOS 数据持久化 关系型数据库之 查询逻辑编写 我们分别编写了 初始化数据库表 增删查改操作 的逻辑代码 那么 下面我们就来整体操作一下 然后 这…

C# OpenCvSharp 图片批量改名

目录 效果 项目 代码 下载 C# OpenCvSharp 图片批量改名 效果 项目 代码 using NLog; using OpenCvSharp; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Windows.Forms; namespace OpenCvSharp_Demo { publi…

微信小程序仿QQ头像轮播效果

1、效果图 2、效果流程分析 1、第1个头像大小从1到0缩小的同时&#xff0c;第2个头像左移 2、上面动画结束后&#xff0c;延迟50ms&#xff0c;第3个头像从0到1放大 3、把头像列表顺序前移一位&#xff0c;并重置轮播状态&#xff0c;以此达到一个循环。然后继续第一个步骤 …

Java旋转矩阵

题目&#xff1a; 给你一幅由 N N 矩阵表示的图像&#xff0c;其中每个像素的大小为 4 字节。请你设计一种算法&#xff0c;将图像旋转 90 度。 不占用额外内存空间能否做到&#xff1f; 示例 1: 给定 matrix [ [1,2,3], [4,5,6], [7,8,9] ], 原地旋转输入矩阵&…

wins10安装ffmpeg

官网下载 点击进入官网&#xff1a;ffmpeg&#xff0c;官网地址&#xff1a;https://ffmpeg.org/download.html 点击上图中Windows图标选中后下面显示的第一行进入如下界面&#xff0c;在release builds第一个绿框里面选择一个版本下载&#xff1a; 下载好之后解压后&#xf…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:PatternLock)

图案密码锁组件&#xff0c;以九宫格图案的方式输入密码&#xff0c;用于密码验证场景。手指在PatternLock组件区域按下时开始进入输入状态&#xff0c;手指离开屏幕时结束输入状态完成密码输入。 说明&#xff1a; 该组件从API Version 9开始支持。后续版本如有新增内容&#…

GO语言-切片底层探索(上)

目录 1.前言 2. 算法题目 错误代码 3. 错误分析 4.总结&#xff1a; 5.正确代码&#xff1a; 6.本地测试代码&#xff1a; 1.前言 今天在力扣上写算法&#xff0c;遇到了一个比较"奇怪"的错误。由于自己使用了递归切片&#xff0c;导致一开始没有看明白&…