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

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

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. 搭建框架

namespace xl {
  template <class Iterator>
  class reverse_iterator {
  private:
    Iterator _it;
  public:
    reverse_iterator(Iterator it)
      : _it(it)
    {}
  };
}

其实框架就一点点啦,

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

3. 迭代器的操作

operator*()

我们直接上手:

operator*() {
  Iterator tmp = _it;
  return *(--tmp);
}

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

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

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

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

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

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

Ref operator*() {
  Iterator tmp = _it;
  return *(--tmp);
}

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

operator->()

Ptr operator->() {
  return &(operator*());
}

operator++()

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

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

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

operator--()

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

operator!=()

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

bool operator!=(const self& s) {
  return _it != s._it;
}

4. 实现 list 的反向迭代器

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()); }

我们来测试一下:

#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 一模一样的操作(直接把代码粘过来即可)

然后我们直接测试:

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)

写在最后:

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

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

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

相关文章
【C++】反向迭代器的实现
【C++】反向迭代器的实现
|
6月前
|
设计模式 编译器 C++
【C++】开始了解反向迭代器
这样我们就实现反向迭代器,大家可以在实际中继续体会。
46 3
|
5月前
|
编译器 容器
反向迭代器的实现
反向迭代器的实现
23 1
|
5月前
|
编译器 C++
C++ 反向迭代器的设计与实现
C++ 反向迭代器的设计与实现
|
6月前
|
设计模式 算法 C++
【C++】STL之迭代器介绍、原理、失效
【C++】STL之迭代器介绍、原理、失效
130 2
|
12月前
|
存储 C++ 容器
|
6月前
|
存储 编译器 C++
【C++/STL】list(常见接口、模拟实现、反向迭代器、)
【C++/STL】list(常见接口、模拟实现、反向迭代器、)
44 0
|
6月前
|
算法 数据处理 C语言
【C++迭代器深度解析】C++迭代器类型之间的继承关系与反向迭代器的独特性
【C++迭代器深度解析】C++迭代器类型之间的继承关系与反向迭代器的独特性
97 0
|
6月前
|
C++ 容器
C++反向迭代器
C++反向迭代器
|
6月前
|
C++
【STL】:反向迭代器
【STL】:反向迭代器
47 0