【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)

写在最后:

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

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

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

相关推荐
过过过呀Glik7 分钟前
在 Ubuntu 上安装 Muduo 网络库的详细指南
linux·c++·ubuntu·boost·muduo
又蓝9 分钟前
使用 Python 操作 Excel 表格
开发语言·python·excel
余~~1853816280021 分钟前
稳定的碰一碰发视频、碰一碰矩阵源码技术开发,支持OEM
开发语言·人工智能·python·音视频
蜀黍@猿1 小时前
【C++ 基础】从C到C++有哪些变化
c++
Am心若依旧4091 小时前
[c++11(二)]Lambda表达式和Function包装器及bind函数
开发语言·c++
明月看潮生1 小时前
青少年编程与数学 02-004 Go语言Web编程 20课题、单元测试
开发语言·青少年编程·单元测试·编程与数学·goweb
zh路西法1 小时前
【C++决策和状态管理】从状态模式,有限状态机,行为树到决策树(一):从电梯出发的状态模式State Pattern
c++·决策树·状态模式
大G哥1 小时前
java提高正则处理效率
java·开发语言
VBA63371 小时前
VBA技术资料MF243:利用第三方软件复制PDF数据到EXCEL
开发语言
轩辰~1 小时前
网络协议入门
linux·服务器·开发语言·网络·arm开发·c++·网络协议