搜索
查看
编辑修改
首页
UNITY
NODEJS
PYTHON
AI
GIT
PHP
GO
CEF3
JAVA
HTML
CSS
搜索
羊村懒王
这个屌丝很懒,什么也没留下!
关注作者
热门标签
jquery
HTML
CSS
PHP
ASP
PYTHON
GO
AI
C
C++
C#
PHOTOSHOP
UNITY
iOS
android
vue
xml
爬虫
SEO
LINUX
WINDOWS
JAVA
MFC
CEF3
CAD
NODEJS
GIT
Pyppeteer
article
热门文章
1
IOS逆向学习-Mach-o_ios mach-o 逆向
2
MQTT协议:如何支持海量的在线IoT设备?_mqtt支持多少连接
3
eNSP配置VRRP(虚拟路由器冗余协议)+ MSTP(多业务传送平台)_ensp mstp+vrrp网络配置
4
2023华为OD面试手撕真题【最长的连续绿色衣服的士兵】_华为面试手撕
5
408数据结构知识点——第八章 排序
6
【深度学习】图像多标签分类任务,百度PaddleClas_百度单图多标签标注
7
优步分布式追踪技术再度精进
8
华为OD机试真题-提取字符串中的最长数学表达式并计算-2023年OD统一考试(C卷)---Python3--开源
9
Leetcode 66.加一_66. 加一
10
基于Spark2.x新闻网大数据实时分析可视化系统项目_基于spark新闻大数据可视化分析
当前位置:
article
> 正文
反向迭代器
作者:羊村懒王 | 2024-04-09 06:02:22
赞
踩
反向迭代器
反向迭代器
反向迭代器涉及适配器模式,所以选择接在栈和队列后面。
接list部分
list反向迭代器部分的源码:
按照正常思路,反向迭代器应该是写一个反向迭代器的类,其中对++的重载写成_node = _node->prev;使得反向迭代器的++就是向前走,但是这样只能针对list起效果。虽然其他的迭代器也可以按各自的逻辑写,但是存在更优的方案:使用适配器模式。
正向迭代器和反向迭代器除了++/--方向相反,其他的没有区别。那么可以通过适配器模式,对正向迭代器进行适配生成反向迭代器,这样可以一劳永逸的解决所有容器的反向迭代器。
reverse_iterator编写在stl_iterator.h中,在删除一些看不懂的代码后,得到的reverse_iterator实现基本逻辑,然后在stl_list.h中找到反向迭代器的实现方式:
由图可见,反向迭代器的成员变量是一个正向迭代器,对反向迭代器解引用得到的是正向迭代器的前一个位置,正向迭代器的end()返回的是末尾元素的下一个位置,而反向迭代器的rbegin()返回的是末尾的元素。之前说的适配器模式指的就是这个,使用rbegin()函数实际上使用的是end(),取得的是末尾元素的下一个位置(哨兵位头结点),但是对反向迭代器解引用应该得到末尾元素。所以反向迭代器的解引用重载使用方式不同于正向迭代器(所以需要重新编写)。如图所示:
rbegin()也可以指向6,但是stl源码没有采用这样的设计,而是用end()去构造rbegin(),用begin()去构造rend()。这叫对称设计。当使用反向迭代器时:
while(rit != rend())
//rit为rbegin()返回值
{
cout << *rit << " ";
rit++;
}
最开始*rit实际访问到的是6,++后指向6,实际访问到的是5,以此类推,当指向1时,访问完毕,rit和rend()相等。
下面的内容为自己实现的list,其中有迭代器,通过迭代器完成反向迭代器的编写
(蓝字部分为新添加的反向迭代器内容)
list.h中
#pragma once
#include<iostream>
#include<algorithm>
#include<assert.h>
using namespace std;
namespace bit
{
template<class T>
struct _list_node
{
_list_node(const T& x = T())
{
_date = x;
_next = nullptr;
_prev = nullptr;
}
_list_node<T>* _next;
_list_node<T>* _prev;
T _date;
};
template<class T, class ref, class ptr>
struct _list_iterator
{
typedef _list_node<T> Node;
typedef _list_iterator<T,ref,ptr> Self;
Node* _node;
_list_iterator(Node* it)
:_node(it)
{}
ref operator*()
{
return _node->_date;
}
ptr operator->()
{
return &(_node->_date);
}
bool operator==(const Self& pos)
{
return _node == pos._node;
}
bool operator!=(const Self& pos)
{
return _node != pos._node;
}
Self& operator++()
{
_node = _node->_next;
return *this;
}
Self operator++(int)
{
Self tmp = _node;
_node = _node->_next;
return tmp;
}
Self& operator--()
{
_node = _node->_prev;
return *this;
}
Self operator--(int)
{
Self tmp = _node;
_node = _node->_prev;
return tmp;
}
};
template<class T>
class list
{
typedef _list_node<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
;
list()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
}
void empty_init()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
}
template <class InputIterator>
list(InputIterator first, InputIterator last)
{
empty_init();
while (first != last)
{
push_back(*first);
first++;
}
}
void swap(list<T>& it)
{
std::swap(_head, it._head);
}
list(const list<T>& it)
{
empty_init();
list<T> tmp(it.begin(), it.end());
swap(tmp);
}
list<T>& operator=(list<T> it)
{
swap(it);
return *this;
}
~list()
{
clear();
delete _head;
_head = nullptr;
}
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it);
}
}
void push_back(const T& x)
{
insert(end(), x);
}
void push_front(const T& x)
{
insert(begin(), x);
}
void pop_back()
{
erase(--end());
}
void pop_front()
{
erase(begin());
}
iterator begin()
{
return iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
const_iterator begin() const
{
return const_iterator(_head->_next);
}
const_iterator end() const
{
return const_iterator(_head);
}
reverse_iterator
rbegin()
{
return
reverse_iterator
(end());
}
reverse_iterator
rend()
{
return
reverse_iterator
(begin());
}
iterator insert(iterator pos, const T& val)
{
Node* newnode = new Node(val);
Node* cur = pos._node;
cur->_prev->_next = newnode;
newnode->_prev = cur->_prev;
newnode->_next = cur;
cur->_prev = newnode;
return iterator(newnode);
}
iterator erase(iterator pos)
{
assert(pos != end());
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
prev->_next = next;
next->_prev = prev;
delete cur;
return iterator(next);
}
private:
Node* _head;
};
}
创建一个新头文件:reverse_iterator.h
reverse_iterator.h中
//整体还相当粗糙
#pragma
once
#include
"list.h"
namespace
bit
{
template
<
class
iterator
,
class
ref
,
class
ptr
>//库中采用了一种复杂的机制规避掉了ref和ptr
struct
Reverse_iterator
{
typedef
Reverse_iterator
self
;
Reverse_iterator(
iterator
x
)
:_it(
x
)
{}
self
&
operator++
()//这里还缺少后缀++,--,const修饰的++,--
{
_it--;
return
*
this
;
}
self
&
operator--
()
{
_it++;
return
*
this
;
}
bool
operator!=
(
Reverse_iterator
x
)
{
return
_it !=
x
._it;
}
ref
operator*
()
{
iterator
it = _it;
return
*(--it);//考虑到保持对称设计,所以访问的位置,是迭代器指向的前一个位置
}
ptr
operator->
()
{
return
&(
operator*
());
}
iterator
_it;
};
}
test.cpp中
#define _CRT_SECURE_NO_WARNINGS 1
#include"list.h"
#include<list>
void test_list1()
{
bit::list<int> l;
l.push_back(1);
l.push_back(2);
l.push_back(3);
l.push_back(4);
l.push_back(5);
bit::list<int>::iterator it = l.begin();
while (it != l.end())
{
cout << *it << " ";
++it;
}
bit::
list
<
int
>::
reverse_iterator
it = l.rend();
while
(it
!=
l.rbegin())//如果在标准库下,将l.rend()和l.rbegin()互换,且将++it改为--it会报错
{
cout
<<
*
it
<<
" "
;
++
it;
}
//提问:既然reverse_iterator采用适配器模式,那么可不可以在这里将list<int>替换成自己写的vector<int>?
//答案是不能,因为没有对vector进行适配支持,即list.h中所有蓝色字体
}
int main()
{
test_list1();
return 0;
}
stl_list.h中存在如下内容
库中list的反向迭代器的实现有两种方式,第一个是新版本的,第二个是旧版本的。新版本和就版本的区别在于新版本只传了迭代器,而旧版本还传了T和T&等,这涉及迭代器萃取,是一种特化。
下面的内容仅做了解。实际当中没什么用途(不是特化没什么用途)
如果这里不写模板参数ref和ptr,会怎么样?
你会发现这两个函数重载的返回值不知道怎么写了。那么标准库中是如何写的呢?
一个标准的迭代器要包含四种类型,即下图中的四种。只有符合这个规范,才能使用迭代器萃取。
具体来说就是需要pointer和reference。
同样可以在stl_list.h中找到
删除Reverse_iterator模板中的ref,ptr,在_list_iterator添加上typedef ptr pointer; typedef ref reference;后就能解决两个重载函数的返回值问题
iterator::reference operator*()
iterator::pointer operator->()
但是这样还是会报错,因为编译器根本不认识iterator::reference和iterator::pointer
原因在于Reverse_iterator的模板参数iterator也是模板,如果编译器允许去iterator中去找,找到的其实就是iterator中的T&和T*,但是iterator实例化后,T得到具体类型(假设是int),但是Reverse_iterator中的T并没有被实例化(对反向迭代器的适配支持中只传了iterator),还是虚拟的T类型。所以编译器直接报错。
编译器在类模板没有实例化之前,不允许去类模板中找内嵌类型(就是typedef过的虚拟类型类型)。找到了也是虚拟类型。后期无法处理。要解决这个问题需要加上关键字typename(在模板中提到过,模板可以用class也可以用typename)
typename iterator::reference operator*()
typename iterator::pointer operator->()
typename在这里的价值就是告诉编译器后面这一串是类型,等到iterator实例化后,再去iterator中找它的内嵌类型。
之后list就能完成迭代器,反向迭代器的功能。
stl_iterator.h中的反向迭代器
又重新typedef了一次,这样在写operator*()的返回值时直接用reference就行。(traits直译是特点,品质,在编程中意译为萃取)
如果Reserve_iterator只传iterator,vector是无法正常运行的。
因为vector的迭代器是指针,是原生类型,不是自定义类型,无法从迭代器中取pointer和reference(iterator会正常typedef ptr和ref,但是Reserve_iterator无法从指针中取pointer和reference)
解决方案有两个:
1.封装一个成员变量为指针的自定义类型迭代器
2.使用萃取,如上图不是直接使用Iterator,而是将Iterator给iterator_traits,iterator_traits又针对Iterator为T*时进行特殊处理(即特化)。Iterator为自定义类型时不做处理。
关于typename
先写一段测试程序(vs2013测不出来,要在vs2019或者g++上测)
template<class T>
void print_list(const list<T>& it)
{
list<T>::const_iterator cit = it.begin();
//会在这里报错
while(cit != it.end())
{
cout << *cit << " ";
cit++;
}
cout << endl;
}
int main()
{
list<int> it;
it.push(1);
it.push(2);
it.push(3);
it.push(4);
print_list(it);
list<string> itstr;
itstr.push("aaa");
itstr.push("bbb");
itstr.push("ccc");
itstr.push("ddd");
print_list(itstr);
return 0;
}
原因在于list<T>是一个类模板,在对print_list模板进行编译时,list<T>还是类模板,没有实例化。编译器不允许到一个未实例化的类模板中找内嵌类型。
要加上typename,即typename list<T>::const_iterator cit = it.begin();
而类模板类型确定的时候就可以不用加typename,不是类模板不能找内嵌类型,而是类模板包含虚拟类型时不能找内嵌类型。
typename list<T>::const_iterator还可以被替换为auto,编译器知道是类型,会在it.begin()返回时自动推导(这个和auto的机制有关,目前知道可以替代就行了)。
声明:
本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:
https://www.wpsshop.cn/w/羊村懒王/article/detail/390756
推荐阅读
article
XilinxISE——
Linces
_
linsence
...
ISE破解版linces,联系哦qq2815298494_
linsence
linsence
IS...
赞
踩
article
Java
内存
模型(
JMM
)_
java
内存
模型...
全称
Java
Memory Model(
JMM
)。我们都知道创建一个对象需要分配
内存
空间并且在不需要该对象时及时回收
内存
...
赞
踩
article
基于
动力学
模型
的无人驾驶
车辆
MPC
轨迹
跟踪
算法
及
carsim
+
matlab
联合仿真学习笔记_实现基于...
车辆
线性时变预测
模型
推导,
MPC
控制器设计、
matlab
代码
逐行分析、
carsim
联合仿真教程_实现基于
车辆
运动学
模型
在...
赞
踩
article
测试
新人
能
少走弯路
就别倔强_
linux
作为现在最流行
的
软件
环境系统
,
一定
需要
掌握
,
目前
的
招聘要求都需...
我是一个刚进入社会
的
测试
新人,学习
测试
的
时候也会焦虑,根本不知道自己
能
够干嘛,自己想干嘛,尤其是不知道市场是不是
需要
自己...
赞
踩
article
20
年
程序员
生涯
,
读了
20
0多本
技术
书
,
挑了几本精华好
书
分享
给大家_
技术
书
分享
...
读
书
好
,
多读
书
,
读好
书
!_
技术
书
分享
技术
书
分享
不知不觉已经又走...
赞
踩
article
工信部
装备
司文件首提
数字
孪生
关键技术
_
数字
孪生
归哪个司局
管
...
2020年4月30日,
工信部
装备
工业二司船舶处发布公告,为《智能船舶标准体系建设指南》征求意见。该建设指南在建设内容之“...
赞
踩
article
Vite
项目插件_
vite
-
plugin
-
monkey
...
文件(浏览器需要访问所有应用到 vue/element api 的页面才会生成所有自动导入 api 的文件 json),...
赞
踩
article
hadoop
2.2
.0集群安装_
java
hotspot(tm)
client
vm warnin...
1. 网络设置禁用防火墙service iptables stop禁用IPv6打开/etc/modprobe.d/dis...
赞
踩
article
react
-
native
an
droid编译报错_
react
-
native
编译报错
failure
:...
报错信息FAILURE: Build
failed
with
an
exception.* What went wron...
赞
踩
article
Java
SE
学习笔记(十八)——
注解
、
动态
代理
...
Java
注解
(Annotation)又称
Java
标注,是JDK 5.0引入的一种注释机制,
Java
语言中的类
、
构造器
、
...
赞
踩
article
Azure
基础
:
什么是
Azure
虚拟
网络
(17)_
udr
azure
...
Azure
虚拟
网络
允许
Azure
资源(例如 VM、Web 应用和数据库)相互通信、与 Internet 上的用户...
赞
踩
article
【
Flink
】流
数据
生成器
(
DataGenerator
)...
Flink
从1.11开始提供了一个内置的DataGen连接器,主要是用于生成一些随机数,用于在没有
数据
源的时候,进行流任...
赞
踩
article
【
华为
OD】C卷真题 200分: 全网首个
100%
,其他都是最高
22.5%
提取
字符串
中的
最长
数学
表...
提取
字符串
中的
最长
合法简单,
字符串
长度
最长
的,并
计算
表达式
的值。如果没有,则返回 0。简单
数学
表达式
只能包含以下内容:0...
赞
踩
article
leetcode
刷题_题目中会给
你
一个
长度
为
n
的
整数
数组
n
um
,
请
你
计算有多少种方法
,
使得
n
...
leetcode
刷题日记
453. 最小操作次数使
数组
元素相等 简单给
你
一个
长度
为
n
的...
赞
踩
article
文献2 基于深度
卷积
网络
的
SAR
图像
目标检测识别_
卷积
神经
网络
sar
图像
目标识别...
基于深度
卷积
网络
的
SAR
图像
目标检测识别0.概述本文主要介绍了使用CNN,Fast RCNN和Faster RCNN 等...
赞
踩
article
ubuntu
20.04 之ros_
noetic
_
robot
_
localization
安装_ubunt...
1.事情是这样的我想运行LIO_SAM编译完后发现自己没有安装
robot
_
localization
,于是就使用命名:su...
赞
踩
article
基于
YOLOV3
和
OpenCV
的
目标
检测
(
Python
C++)[译]_基于
opencv
/c++...
原文:Deep Learning based Object Detection using YOLOv3 with Op...
赞
踩
article
C#实现
Word
转PDF_
c#
调用
wps
打印机把
word
抓换成
pdf
...
本文主要是采用C#将
wps
文件转为PDF。需要提前安装好WPS,并在程序中添加引用using Microsoft.Off...
赞
踩
article
数字
孪生
的背景_
数字
孪生
背景...
美国宇航局为了解决飞行器开发过程中的潜在问题,开发了飞行器的硬件
数字
孪生
体——“铁鸟”(飞控液压系统综合实验台架)作为飞...
赞
踩
article
Windows
Azure
体验之新建
虚拟
网络
...
虚拟
网络
是可以在
Azure
中配置的
网络
覆盖。同一个
虚拟
网络
中的 VM 和服务可以相互访问。但是,该
虚拟
网络
外部的服务...
赞
踩
相关标签
ISE
Linces
破解
qq
java
jvm
自动驾驶
matlab
目标跟踪
程序人生
测试工程师
软件测试
单元测试
python
开发语言
程序员
编程语言
经验分享
神经网络
人工智能
大数据
运维
vue.js
javascript
前端