【STL】模拟实现反向迭代器

目录

[1. 读源码](#1. 读源码)

[2. 搭建框架](#2. 搭建框架)

[3. 迭代器的操作](#3. 迭代器的操作)

operator*()

operator->()

operator++()

operator--()

operator!=()

[4. 实现 list 的反向迭代器](#4. 实现 list 的反向迭代器)

[5. 实现 vector 的反向迭代器](#5. 实现 vector 的反向迭代器)

[6. 源码分享](#6. 源码分享)

写在最后:


1. 读源码

我们之前实现的 vector,list 好像都只实现了他们的正向迭代器,那有正向,

会有反向迭代器这种东西吗?答案是有的,那我们该怎么实现呢?

实际上根据我们之前的经验,根据每个容器的特色实现一份就好了,

但是之前在读源码的时候,好像并没有看到源码实现,这是怎么一回事呢?

来读读 list 的源码是怎么做的:

我们在这里找到了 list 的反向迭代器,他没有自己实现,

而是直接使用了这个 reverse_iterator 的类模板,这又是怎么一回事呢?

我们在 STL 源码的 stl_iterator.h 文件中找到了答案:

这里面竟然存在这样一个类,reverse_iterator,难道说其他的容器,

也都不是自己实现一份反向迭代器,而是直接使用这个类吗?

想到这里,我们赶紧去看一眼 vector 的源码:

发现了没有,他们的调用方式是一模一样的,都是同样的调用,

使用的类型是他们各自的迭代器类型,通过模板的特性,

让编译器实例化出不同的代码,达成实现他们各自的迭代器的目的,

非常巧妙的设计,只一份代码,让所有的容器的反向迭代器都复用,

那他是怎么实现的呢?我们来看看他实现的源码:

我们可以看到这个就是迭代器类型的成员变量了。

还是老规矩,看完成员变量,我们去看一下核心的接口实现,

先来看 ++ 的重载 :

他的 ++ 就是让原先的正向迭代器执行 -- 操作,没毛病,

再来看 -- 的重载:

也没毛病,我们再来看看他的解引用操作:

这下怪了,为啥解引用取到的位置是原先位置的前一个位置呢?

那我们就得去看看那些容器的反向迭代器是怎么确定初始位置的了,

先来看 list 的:

正向迭代器的 begin 就是第一个位置,end 就是哨兵位的头结点,

但是 rbegin 用的是 end 来初始化反向迭代器,也就是他在 end 的位置(哨兵位上)

而 rend 用的是 begin 来初始化反向迭代器,也就是他在 begin 的位置(第一个位置上)

我们也可以看一下 vector 是不是这样的:

可以看到也是这样的。

那这样好像不太对啊,如果直接使用迭代器,

第一次解引用就会出问题:(这里我写段伪代码感受一下)

rit = rbegin()

while(rit != rend()) {

cout << *rit << endl; // 这里就出问题了

rit++;

}

所以库里在实现的解引用操作符的时候,解引用的就是他的前一个位置,

我再截出来看一眼:

现在看的也差不多了,马上开始动手实现吧~

2. 搭建框架

cpp 复制代码
namespace xl {
	template <class Iterator>
	class reverse_iterator {
	private:
		Iterator _it;

	public:
		reverse_iterator(Iterator it)
			: _it(it)
		{}
	};
}

其实框架就一点点啦,

就是写个成员变量,写个构造函数就差不多完成啦。

3. 迭代器的操作

operator*()

我们直接上手:

cpp 复制代码
operator*() {
	Iterator tmp = _it;
	return *(--tmp);
}

这个时候我们遇到了一些困难,

我们该怎么返回这个值呢?还是跟之前一样我们需要让他支持 const 类型,

先来看看源码是怎么实现的:

他这里的 reference 通过了一系列的复杂操作然后套了出来,

这里他用的操作是萃取,比较的麻烦,还涉及模板的特化,

所以我们打算用一个比较方便暴力的方法,就是通过模板参数直接传过来:

cpp 复制代码
Ref operator*() {
	Iterator tmp = _it;
	return *(--tmp);
}

这样下面的实现也解决了:

operator->()

cpp 复制代码
Ptr operator->() {
	return &(operator*());
}

operator++()

这里我们再顺便把类名 typedef 一下,因为他太长了:

然后来实现 ++ :(这里我都是前置和后置都实现了)

cpp 复制代码
self& operator++() {
	--_it;
	return *this;
}

self operator++(int) {
	self tmp = _it;
	--_it;
	return tmp;
}

operator--()

cpp 复制代码
self& operator--() {
	++_it;
	return *this;
}

self operator--(int) {
	self tmp = _it;
	++_it;
	return tmp;
}

operator!=()

直接用正向迭代器的 != 比较即可:

cpp 复制代码
bool operator!=(const self& s) {
	return _it != s._it;
}

4. 实现 list 的反向迭代器

cpp 复制代码
typedef reverse_iterator<iterator, const T&, const T*> const_reverse_iterator;
typedef reverse_iterator<iterator, T&, T*> reverse_iterator;

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

我们来测试一下:

cpp 复制代码
#include "iterator.h"
#include "list.h"

int main()
{
	xl::list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);

	for (auto e : lt) cout << e << " ";
	cout << endl;

	xl::list<int>::reverse_iterator rit = lt.rbegin();
	while (rit != lt.rend()) {
		cout << *rit << " ";
		++rit;
	}
	cout << endl;

	return 0; 
}

输出:

输出没毛病~

5. 实现 vector 的反向迭代器

跟 list 一模一样的操作(直接把代码粘过来即可)

然后我们直接测试:

cpp 复制代码
void test_vector() {
	xl::vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);

	for (auto e : v) cout << e << " ";
	cout << endl;

	xl::vector<int>::reverse_iterator rit = v.rbegin();
	while (rit != v.rend()) {
		cout << *rit << " ";
		++rit;
	}
	cout << endl;
}

输出:

结果也没毛病~

6. 源码分享

模拟实现简易STL: 模拟实现简易STL (gitee.com)

写在最后:

以上就是本篇文章的内容了,感谢你的阅读。

如果感到有所收获的话可以给博主点一个哦。

如果文章内容有遗漏或者错误的地方欢迎私信博主或者在评论区指出~

相关推荐
凤枭香1 分钟前
Python OpenCV 傅里叶变换
开发语言·图像处理·python·opencv
ULTRA??5 分钟前
C加加中的结构化绑定(解包,折叠展开)
开发语言·c++
远望清一色21 分钟前
基于MATLAB的实现垃圾分类Matlab源码
开发语言·matlab
confiself31 分钟前
大模型系列——LLAMA-O1 复刻代码解读
java·开发语言
凌云行者40 分钟前
OpenGL入门005——使用Shader类管理着色器
c++·cmake·opengl
XiaoLeisj42 分钟前
【JavaEE初阶 — 多线程】Thread类的方法&线程生命周期
java·开发语言·java-ee
凌云行者44 分钟前
OpenGL入门006——着色器在纹理混合中的应用
c++·cmake·opengl
杜杜的man1 小时前
【go从零单排】go中的结构体struct和method
开发语言·后端·golang
幼儿园老大*1 小时前
走进 Go 语言基础语法
开发语言·后端·学习·golang·go
半桶水专家1 小时前
go语言中package详解
开发语言·golang·xcode