【C++】反向迭代器的实现

简介: 【C++】反向迭代器的实现

1.迭代器的分类


我们随便打开一个容器,看迭代器相关的接口,都可以发现,支持迭代器的容器,其迭代器有以下几类

  • 正向迭代器
  • const正向迭代器
  • 反向迭代器
  • const反向迭代器

4984626eaa68287e8f8e654c43a1acaa.png


2.反向迭代器的使用


对于正向迭代器,我么已经很熟悉了,在之前的STL容器的模拟实现中都已经实现过了。那么现在来看看反向迭代器

❗❗反向迭代器的特点

✅反向迭代器的迭代顺序与正向迭代器相反

✅rbegin()相当于end()

✅rend()相当于begin()

✅反向迭代器++相当于正向迭代器–


那么现在,让我们来看一看反向迭代器的使用与正向迭代器的区别

void Test1()
{
  vector<int> v;
  v.push_back(1);
  v.push_back(2);
  v.push_back(3);
  v.push_back(4);
  v.push_back(5);
  v.push_back(6);
  auto rit = v.rbegin();
  while (rit != v.rend())
  {
    cout << *rit << " ";
    ++rit;
  }
  cout << endl;
}


5bd21f7df015f27f705cd71bb5950c37.png

可以看到,反向迭代器的使用和正向迭代器没有任何区别,只是迭代的顺序不同。


3.反向迭代器的模拟实现


对于反向迭代器的实现,我们去参考一下库里面是怎么实现的

注:这里参照的库是SGI的3.0版本,与侯捷老师的STL源码剖析相对应

e96c10b99482984709c0ba5a5e8155c5.png

这里以list的反向迭代器实现为例,可以看到库里面对反向迭代器的实现是通过实例化reverse_iterator类来实现的,通过用法可以推测出来reverse_iterator类模板的实现逻辑是通过传入的正向迭代器的类型来实例化出不同类的反向迭代器,这里反向迭代器就可以理解成一个容器适配器。


下面来看一下reverse_iterator类的实现,这个类在头文件stl_iterator中

04c717a68b9b237c38b5c64861bb95f9.png

通过观察库里面的代码,对模拟实现反向迭代器已经大概有了了解,但是其中出现了新语法:萃取,笔者正在学习的路上,目前的能力对萃取的理解有难度,所以本次模拟实现就不使用萃取的方式,而是使用和list迭代器同样的解决方案——传模板参数的方式解决【C++】list的模拟实现


所以,我们可以得到反向迭代器类的框架如下

template<class Iterator, class Ref, class Ptr>//这里对参数的设计参考之前list迭代器的这几
class reverse_iterator
{
    typedef reserve_iterator<Iterator, Ref, Ptr> Self;//这里类型实例化出来太长了,做一个重命名
public:
  //......  
private:
    Iterator _it;
};


成员函数的实现

参照list迭代器类的成员函数,我们需要设计的成员函数有:

  • 解引用
  • 前置后置的++–
  • ->运算符
  • != 和==


按照我们的理解,很轻松能够写出来以下的成员函数。

reverse_iterator(Iterator it)//构造函数
    :_it(it)
    {}
Self& operator++()//前置++
{
    --_it;
    return *this;
}
Self operator++(int)//后置++
{
    Self tmp = _it;
    --_it;
    return tmp;
}
Self& operator--()//前置--
{
    ++_it;
    return *this;
}
Self operator--(int)//后置--
{
    Self tmp = _it;
    ++_it;
    return tmp;
}
Ref operator*()
{
    return *this;
}
Ptr operator->()
{
    return &(operator*());
}
bool operator!=(const Self& s) const
{
    return _it != s._it;
}
bool operator==(const Self& s) const
{
    return _it == s._it;
}


接下来和库里面的对比以下吧

8b3038c7bc2af0fba10a732d21bc9ee7.png

诶?这和我们实现的不一样啊,为啥嘞?“源码之下了无秘密”,我们去看一下list中对迭代器接口的实现

15bd82e7d53838d481e8491538854142.png

哦哦懂了,原来是这样,在list中对rbegin的实现是使用end()构造的,end返回的是最后一个元素的下一个位置

fd453accfabc76ffb96ed7488127d0fc.png

如果我们要拿到最后一个元素,会调用rbegin()接口,此时如果要拿到最后一个元素,当然要让rbegin先–,指向最后一个元素之后再解引用。

所以最终对于*得到了以下实现,其余不用改变

当然,如果你愿意更改所有容器对反向迭代器的传参,也可以按照上文中的实现方式实现。

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


最后,我们对返现迭代器类的实现完整代码如下

namespace zht
{
  template<class Iterator, class Ref, class Ptr>//这里对参数的设计参考之前list迭代器的这几
  class reverse_iterator
  {
    typedef reserve_iterator<Iterator, Ref, Ptr> Self;//这里类型实例化出来太长了,做一个重命名
  public:
    reverse_iterator(Iterator it)//构造函数
      :_it(it)
    {}
    Self& operator++()//前置++
    {
      --_it;
      return *this;
    }
    Self operator++(int)//后置++
    {
      Self tmp = _it;
      --_it;
      return tmp;
    }
    Self& operator--()//前置--
    {
      ++_it;
      return *this;
    }
    Self operator--(int)//后置--
    {
      Self tmp = _it;
      ++_it;
      return tmp;
    }
    Ref operator*()
    {
      Iterator tmp = _it;
      return *(--tmp);
    }
    Ptr operator->()
    {
      return &(operator*());
    }
    bool operator!=(const Self& s) const
    {
      return _it != s._it;
    }
    bool operator==(const Self& s) const
    {
      return _it == s._it;
    }
  private:
    Iterator _it;
  };
}


4.list类的反向迭代器实现


有了上述迭代器类的实现,我们现在可以顺便把之前模拟实现的list类中的反向迭代器加上了

typedef zht::reverse_iterator<iterator, T&, T*> reverse_iterator;//反向迭代器
typedef zht::reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;//const反向迭代器
reverse_iterator rbegin()
{
    return reverse_iterator(end());
}
reverse_iterator rend()
{
    return reverse_iterator(begin());
}
const_reverse_iterator rbegin() const
{
    return const_reverse_iterator(end());
}
const_reverse_iterator rend() const
{
    return const_reverse_iterator(begin());
}

e789a16e8b59018f1f768b9f4b9e86e4.png

至此,就把STL的迭代器完整实现了。

相关文章
|
关系型数据库 MySQL 数据库
Linux C/C++ 开发(学习笔记七):Mysql数据库C/C++编程实现 插入/读取/删除
Linux C/C++ 开发(学习笔记七):Mysql数据库C/C++编程实现 插入/读取/删除
531 0
|
Kubernetes 安全 Docker
使用容器服务Kubernetes 与 自建Kubernetes的区别
自建K8S有哪些麻烦呢,容器服务的K8S能否解决这些麻烦呢?
11695 0
|
存储 Kubernetes 数据管理
Fluid 携手 Vineyard,打造 Kubernetes 上的高效中间数据管理
本文阐述了如何利用 Fluid 和 Vineyard 在 Kubernetes 上优化中间数据管理,解决开发效率、成本和性能问题。 Fluid 提供数据集编排,使数据科学家能用 Python 构建云原生工作流,而 Vineyard 通过内存映射实现零拷贝数据共享,提高效率。两者结合,通过数据亲和性调度减少网络开销,提升端到端性能。 同时通过一个真实事例介绍了安装 Fluid、配置数据与任务调度及使用 Vineyard 运行线性回归模型的步骤,展示了在 Kubernetes 上实现高效数据管理的实践方法。未来,项目将扩展至 AIGC 模型加速和 Serverless 场景。
386 0
RestSharp编写api接口测试,并实现异步调用(不卡顿)
【7月更文挑战第14天】以下是使用 `RestSharp` 进行 API 接口测试并实现异步调用的示例代码,以避免发送请求和等待响应过程中导致的界面或程序卡顿。关键步骤包括:创建 `RestClient` 并指定基础 URL;创建 `RestRequest` 并指定端点及方法;使用 `ExecuteAsync` 异步发送请求,并通过 `await` 等待响应。 对于特定需求,如需测试获取用户信息的 API,可在请求中添加身份验证头或查询参数。 通过灵活配置请求参数和处理响应,可以满足各种 API 测试的需求。
272 10
|
存储 Serverless C语言
【C语言】时间函数详解
在C语言中,时间处理功能由标准库 `time.h` 提供。使用这些函数时,需要包含 `#include <time.h>` 头文件。以下是一些常用的时间函数的详细讲解,包括函数原型、参数说明、返回值说明以及示例代码和表格汇总。
440 9
|
12月前
|
Kubernetes 算法 调度
阿里云 ACK FinOps成本优化最佳实践
本文源自2024云栖大会梁成昊演讲,讨论了成本优化策略的选择与实施。文章首先介绍了成本优化的基本思路,包括优化购买方式、调整资源配置等基础策略,以及使用弹性、资源混部等高级策略。接着,文章详细探讨了集群优化和应用优化的具体方法,如使用抢占式实例降低成本、通过资源画像识别并优化资源配置,以及利用智能应用弹性策略提高资源利用效率。
|
机器学习/深度学习 人工智能 数据格式
Make-It-Animatable:中科大联合腾讯推出的自动生成即时动画准备资产
Make-It-Animatable是由中国科学技术大学和腾讯联合推出的数据驱动框架,能够在不到一秒内将任何3D人形模型转换为可用于动画的状态。该框架支持多种3D数据格式,并采用从粗到细的表示策略和结构感知建模,显著提升了动画准备的质量和速度。
222 7
Make-It-Animatable:中科大联合腾讯推出的自动生成即时动画准备资产
在Linux中,如何查看系统当前运行的进程以及它们的状态?
在Linux中,如何查看系统当前运行的进程以及它们的状态?
|
SQL 开发框架 .NET
ASP连接SQL数据库:从基础到实践
随着互联网技术的快速发展,数据库与应用程序之间的连接成为了软件开发中的一项关键技术。ASP(ActiveServerPages)是一种在服务器端执行的脚本环境,它能够生成动态的网页内容。而SQL数据库则是一种关系型数据库管理系统,广泛应用于各类网站和应用程序的数据存储和管理。本文将详细介绍如何使用A
290 3
|
数据采集 安全 API
数据治理:实现原始数据不出域,确保数据可用不可见的创新策略
在数字化时代,数据成为企业宝贵资产,驱动业务决策与创新。然而,数据量激增和流通频繁带来了安全和管理挑战。“原始数据不出域,数据可用不可见”的治理理念应运而生,通过数据脱敏、沙箱技术和安全多方计算等手段,确保数据安全共享与高效利用。这一理念已广泛应用于金融、医疗等行业,提升了数据价值和企业竞争力。
2208 0