自己动手写C++迭代器

2023-05-16

综述

关于STL iterator和 iterator adapter 的部分我已在先前的博客 stl源码剖析笔记之iterator 中有所提及,下面我们可以试着自己动手写一个简单的迭代器工具 step_iterator,以深入对iterator的理解。

step_iterator 可以接受两个模板参数,一个是迭代器 Iter,一个是步进距离 Step,相较于普通的迭代器,它可以以 Step 的距离前进或者后退。

我们将通过 std::iterator 和 boost::iterator_adaptor 两种方式来实现上述功能的迭代器。

继承 std::iterator

STL源码中定义了 std::iterator,以对 iterator 的型别做出规范。

//stl_iterator.h
template <class _Category, class _Tp, class _Distance = ptrdiff_t,
          class _Pointer = _Tp*, class _Reference = _Tp&>
struct iterator {
  typedef _Category  iterator_category;
  typedef _Tp        value_type;
  typedef _Distance  difference_type;
  typedef _Pointer   pointer;
  typedef _Reference reference;
};

《STL源码剖析》一书中提到,我们可以用模板继承的方式构建迭代器,可以省去一些代码书写。这样我们就可以这样定义step_iterator :

template <class _Iter, std::ptrdiff_t _Step=2>
class step_iterator2 : public std::iterator<typename std::iterator_traits<_Iter>::iterator_category, typename std::iterator_traits<_Iter>::value_type>{
    private:
            _Iter m_iter;
            typedef  step_iterator2<_Iter, _Step> Self;
            typedef std::iterator<typename std::iterator_traits<_Iter>::iterator_category, typename std::iterator_traits<_Iter>::value_type> BaseIter;
    public:
            using typename BaseIter::reference;
            using typename BaseIter::pointer;
            using typename BaseIter::difference_type;
}

这里我们定义了一个 BaseIter 简化基类 iterator 的书写。以及 using 是必须的,否则虽然基类模板中定义了reference,pointer等类型,但是派生模板并不能直接使用。

使用 iterator_traits

如果不用继承的方式,就应该像下面这么写,使用 iterator_traits 萃取出各个特性。

template <class _Iter, std::ptrdiff_t _Step=2>
class step_iterator2{
    private:
            _Iter m_iter;
            typedef  step_iterator2<_Iter, _Step> Self;
    public:
            typedef typename std::iterator_traits<_Iter>::iterator_category iterator_category;
            typedef typename std::iterator_traits<_Iter>::value_type value_type;
            typedef typename std::iterator_traits<_Iter>::pointer pointer;
            typedef typename std::iterator_traits<_Iter>::reference reference;
            typedef typename std::iterator_traits<_Iter>::difference_type difference_type;

}

这里多提一句,似乎 typedef typename _Iter::iterator_category iterator_category 也可以萃取出特性,写法还更简略些,那么为什么不这么写呢?因为还要考虑到模板参数是指针的情况,而 iterator_traits 有特化版本,可以考虑到各种情况。

虽然说可以用模板继承方式构建迭代器,但实际上,一些常用的迭代器工具,如 std::ostream_iterator , std::reverse_iterator 等,并没有用模板继承。

实现与测试

接下来我们需要实现 iterator 的几个必须实现的接口

template <class _Iter, std::ptrdiff_t _Step=2>
class step_iterator2 : public std::iterator<typename std::iterator_traits<_Iter>::iterator_category, typename std::iterator_traits<_Iter>::value_type>{
    private:
            _Iter m_iter;
            typedef  step_iterator2<_Iter, _Step> Self;
            typedef std::iterator<typename std::iterator_traits<_Iter>::iterator_category, typename std::iterator_traits<_Iter>::value_type> BaseIter;
    public:
            using typename BaseIter::iterator_category;
            using typename BaseIter::value_type;
            using typename BaseIter::reference;
            using typename BaseIter::pointer;
            using typename BaseIter::difference_type;
    public:
            step_iterator2(_Iter i):m_iter(i){}

            reference operator*(){ return *m_iter; }
            pointer operator->(){ return m_iter; }
            Self& operator++(){
                std::advance(m_iter, _Step);
                return *this;
            }
            Self operator++(int){
                Self temp=this;
                std::advance(m_iter, _Step);
                return temp;
            }

            Self& operator--(){
                std::advance(m_iter,-_Step);
                return *this;
            }
            Self operator--(int){
                Self temp=this;
                std::advance(m_iter, -_Step);
                return temp;
            }
            bool operator!=(const Self& other) const{
                return m_iter!=other.m_iter;
            }
            difference_type operator-(const Self& other){
                return std::distance(other.m_iter,m_iter);
            }

};

写一个测试文件,测试一下效果。

//test.cpp
#include "step_iterator.h"
#include <vector>
#include <iostream>

int main(){
        using namespace std;
        vector<int> a={1,2,3,4,5,6};
        copy(a.begin(),a.end(),ostream_iterator<int>(cout," ")); //1 2 3 4 5 6
        cout<<endl;
        step_iterator2<typename vector<int>::iterator> s3(a.begin()), s4(a.end());
        copy(s3, s4, ostream_iterator<int>(cout," "));  //1 3 5 0 0
        cout<<endl;
        return 0;
}

然而,打印并不是 1 3 5,细究一下,原因出在 copy() 这个全局模板函数上面。我们看 copy() 的源码核心部分如下:

//stl_algo.h
for(Distance n=last- first; n>0; --n, ++result, ++first)
  *result=*first;

for循环次数是由 last-first 决定的,然而对于 step_iterator 来说, operator- 是计算 m_iter 迭代器之间的距离,显然就不对了。所以我们应该再改进一下。

difference_type operator-(const Self& other){
    return std::distance(other.m_iter,m_iter)/_Step;
}

对于此测试例来说, step_iterator 已经初步成型了。然而这样的 step_iterator 并不完美,还有很多成员函数没有实现,例如 operator+= , operator-=,实际上这些操作符都有很多共通的地方,一个个去写会很麻烦,有没有更好的解决办法呢?Boost库提供了一些好的思路。

boost::iterator_adaptor

首先有个小小的地方想吐槽一下,就是这个 adaptor 的写法,《STL源码剖析》中用的一直都是adapter,到了boost这里突然变成了 adaptor,总之,不要在意这些细节吧。

罗剑锋的《Boost程序库探秘》第三章探究了迭代器基类模板 iterator_facade 和 iterator_adaptor 的一些用法。iterator_facade 顾名思义,用了设计模式中外观模式的思想,给 iterator 重新披上一层外衣(它并不是提供新的功能,而是将原有的操作符和成员函数封装成另一个接口)

摘抄Boost文档中关于 iterator_facase 的说明如下:

Although it is easy to create iterators that almost conform to the standard, the iterator requirements contain subtleties which can make creating an iterator which actually conforms quite difficult. Further, the iterator interface is rich, containing many operators that are technically redundant and tedious to implement. To automate the repetitive work of constructing iterators, we propose iterator_facade, an iterator base class template which provides the rich interface of standard iterators and delegates its implementation to member functions of the derived class.

大意是, iterator_facade 提供了丰富的标准迭代器接口,并且将它们的实现委托到派生类的成员函数中。例如之前说的, operator++ 和 operator+= 中共通的代码,就可以放到 increment() 成员函数中让派生类自己去实现。

派生的迭代器类要实现以下成员函数:

  • dereference()
  • equal()
  • increment()
  • decrement()
  • advance()
  • distance_to()

而标准 iterator 的一些成员函数和操作符,都转换为对以上核心成员函数的调用。

iterator_adaptor 继承自 iterator_facade ,接受一个 Adaptee 成员,通常是一个迭代器对象,可以借助它来完成核心成员函数的缺省实现。

被适配的迭代器称为 Adaptee ,iterator_adaptor 有一个Adaptee类型的私有成员 m_iterator ,派生类可以用 base_reference() 或者 base() 访问它。

iterator_adaptor 的模板参数如下

template<
    class Derived,                              //迭代器子类名
    class Adaptee,                              //被适配的迭代器
    class Value                 = use_default,  //值类型
    class CategoryOrTraversal   = use_default,  //迭代器分类标志
    class Reference             = use_default,  //迭代器值引用类型
    class Difference            = use_default,  //迭代器距离类型
>

我们可以以继承 iterator_adaptor的方式实现 step_iterator:

template <class _Iter, std::ptrdiff_t _Step=2>
class step_iterator1 : public boost::iterator_adaptor<step_iterator1<_Iter,_Step>, _Iter>{
    private:
            typedef  step_iterator1<_Iter, _Step> Self;
            using typename step_iterator1::iterator_adaptor_::difference_type; 

    public:
            step_iterator1(_Iter i):step_iterator1::iterator_adaptor_(i){}
            void increment(){
                this->base_reference()+=_Step;
            }   
            void decrement(){
                this->base_reference()-=_Step;
            }   
            difference_type distance_to(const  Self& other) {
                return distance_to(this->base_reference(), other.base_reference())/_Step;
            }   

};

相较继承 std::iterator 的方式,是不是简明了很多呢?事实上,Boost库常用的迭代器工具,都继承自 iterator_adaptor 。

参考

stackoverflow-How to correctly implement custom iterators and const_iterators?

stackoverflow-I inherit from std::iterator, but compiler does not recognise ‘pointer’ or ‘reference’

Boost文档-iterator_facade
Boost文档-Iterator Facade and Adaptor

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

自己动手写C++迭代器 的相关文章

  • 安卓10(Android10\API29)保存图片到相册DCIM/Camera

    大家都知道Android10最大的变化可能就是Scoped Storage xff08 分区存储 xff09 对于把图片保存到相册的应用 xff0c 影响就大了 xff0c 因为这个功能在Android10的手机上就会出现异常了 xff0c
  • Android保存自定义路径的图片的一些问题

    解决11保存到相册的问题 android 11 版本下图片的保存方式改变以及保存图片到相册 笑忘书客的博客 CSDN博客 android11保存图片到相册 首先是权限的问题 在android 10以后 xff0c 权限进一步更新 xff0c
  • 在线安装eclipse中html/jsp/xml editor插件(非常可靠),eclipseeditor

    在线安装eclipse中html jsp xml editor插件 xff08 非常可靠 xff09 xff0c eclipseeditor 之前有一篇文章也是安装eclipse中的web开发插件 xff0c 但是经过很多人使用 xff0c
  • android 基于ijkplayer项目进行的播放器

    jjdxm ijkplayer 项目地址 xff1a jjdxm ijkplayer 简介 xff1a 基于 ijkplayer 简单的 UI 界面 当前项目是基于 ijkplayer 项目进行的播放器界面 UI 封装 是一个适用于 And
  • android 类似豆瓣读书,提供一个书籍查看、搜索、交流的平台

    SoleBooks 项目地址 xff1a Blankeer SoleBooks 简介 xff1a 类似豆瓣读书 xff0c 提供一个书籍查看 搜索 交流的平台 LeanCloud MVP RxJava 截图 项目描述 数据来源 数据来源于豆
  • C++中::与.的区别

    xff08 1 xff09 是域作用符 xff0c 是各种域性质的实体 xff08 比如类 xff08 不是对象 xff09 名字空间等 xff09 调用其成员专用的 xff08 如果有个局部变量与全局变量同名 xff08 假设都是int
  • Java防止SQL注入

    1 定义 xff1a 所谓SQL注入 xff0c 就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串 xff0c 最终达到欺骗服务器执行恶意的SQL命令 2 防止SQL注入的方法 xff1a A xff1a 使用Pre
  • 高仿萌聚 app ,内容简直是宅男福利啊

    mengqu 项目地址 xff1a panacena mengqu 简介 xff1a 高仿萌聚 app xff0c 内容简直是宅男福利啊 xff01 高仿萌趣 app 最近下了个叫做 萌趣 的 app xff0c 内容简直是宅男福利啊 xf
  • 人脸识别扫描(活体检测功能,眨眼、摇头、点头),身份证认证

    FaceAC 项目地址 xff1a sxpl FaceAC 简介 xff1a 人脸识别扫描 xff08 活体检测功能 xff0c 眨眼 摇头 点头 xff09 xff0c 身份证认证 更多 xff1a 作者 提 Bug 标签 xff1a 人
  • 全开源即时通讯(IM)系统 高仿微信

    android chat 项目地址 xff1a wildfirechat android chat 简介 xff1a 全开源即时通讯 IM 系统 高仿微信 更多 xff1a 作者 提 Bug 官网 标签 xff1a 野火 IM 是一套跨平台
  • OpenCV与机器视觉

    最近在网易云课堂把南科大于仕琪团队的OpenCV教程完整看了一遍 xff0c 对图像处理或者机器视觉又有了一个系统性的理解 OpenCV中文网站就是他创建的 xff0c 他的研究团队及其相应成果可以在个人网站中查阅 回想过去在图像处理方面的
  • Virtual Box+Ubuntu20.04+ROS2 Foxy配置

    ROS从最早的正式版本Box Turtle到现在也十几年了 而ROS2出来也挺久了 xff0c 一直没机会看看 好久也没弄ROS xff0c 这几天捣鼓了捣鼓 目录 1 Virtual Box安装Ubuntu20 04 2 ROS2 Fox
  • TI CC265x的IIC通讯读取IMU BMI08x数据

    SmartLink CC265x是TI公司出的无线MCU平台器件 最近玩了个小项目用TI的CC265x平板IIC接口通讯 xff0c 获取博世BMI08x陀螺仪 加速度计传感器的数据 本篇博客亦是对博客 树莓派IIC通讯获取BMI08x I

随机推荐

  • 三种方法在ROS中加载Qt库进行GUI设计

    编写ros程序 xff0c 因为有时会涉及到界面设计 xff0c 所以本人主要用的QtCreator IDE 首先当然是安装QtCreator xff0c 这个网上有很多安装教程和下载资源 xff0c 非常简单 由于Qt的工程大多采用qma
  • 在ROS中处理yaml文件

    ROS中的参数服务器 xff08 Parameter Server xff09 的相关操作可参见roscpp tutorials Tutorials Parameters 如果想要载入参数 xff0c 可以通过编写yaml文件 xff0c
  • ROS动态调参(dynamic reconfigure)客户端服务端之C++ Python实现

    在ROS系统中 xff0c 我们需要实时修改参数 xff0c 并能马上看到运行效果 这一功能是通过ros dynamic reconfigure包实现的 官网教程如下 xff1a dynamic reconfigure Tutorials
  • ROS中slam_gmapping、map_server源码解读及其librviz的使用

    SLAM全称simultaneous localization and mapping xff0c 即实时定位与地图构建 也就是说导航离不开地图 xff0c 目前常用的地图构建方法有三种 xff1a 1 gmapping xff0c 一种基
  • 记 - PC视频播放最强画质教程(Potplayer + madVR)

    PC视频播放最强画质教程 前言 xff1a 本次使用到的软件 工具 Potplayer播放器 Potplayer是目前我用到的最好用的宝藏视频播放软件 xff1a 内存占用低 无广告 支持视频格式多 功能强大 扩展性高 界面唯美 xff08
  • 【三维深度学习】多视角立体视觉 MVSNet代码解读

    MVSNet通过将相机几何参数编码到网络中 xff0c 实现了端到端的多视角三维重建 xff0c 并在性能和视觉效果上超越了先前算法 xff0c 并在eccv2018 oral中发表 模型主要包含四个主要步骤 xff1a 图像特征抽取 多视
  • 【pandas】删除满足条件元素所在的行

    在数据清洗时 xff0c 需要按照一定条件删除某些数据样本 xff0c 利用布尔表达式 索引和drop方法可以实现 1 pandas drop df 61 df drop df lt some boolean condition gt in
  • 【AI视野·今日Robot 机器人论文速览 第八期】Wed, 16 Jun 2021

    AI视野 今日CS Robotics 机器人学论文速览 Wed 16 Jun 2021 Totally 13 papers x1f449 上期速览 更多精彩请移步主页 Daily Robotics Papers Constrained Mo
  • 【CVPR2022】论文列表与下载——PartTwo

    CVPR2022将于6月22日召开 x1f389 x1f389 x1f389 xff0c 本次会议共收录了2067篇论文 由于数量较多 xff0c 本文将分四个子文章呈现 xff0c 可直接点击论文标题获取文档 x1f4c3 第一部分 x1
  • 【CVPR2022】论文列表与下载——PartThree

    CVPR2022将于6月22日召开 x1f389 x1f389 x1f389 xff0c 本次会议共收录了2067篇论文 由于数量较多 xff0c 本文将分四个子文章呈现 xff0c 可直接点击论文标题获取文档 x1f4c3 第一部分 x1
  • 【CVPR2022】论文列表与下载——PartFour

    CVPR2022将于6月22日召开 x1f389 x1f389 x1f389 xff0c 本次会议共收录了2067篇论文 由于数量较多 xff0c 本文将分四个子文章呈现 xff0c 可直接点击论文标题获取文档 x1f4c3 第一部分 x1
  • Python三维绘图--Matplotlib

    Python三维绘图 在遇到三维数据时 xff0c 三维图像能给我们对数据带来更加深入地理解 python的matplotlib库就包含了丰富的三维绘图工具 1 创建三维坐标轴对象Axes3D 创建Axes3D主要有两种方式 xff0c 一
  • 【深度学习】三维点云数据集总结

    点云数据集总结 三维点云数据 xff0c 三维深度学习 1 ShapeNet ShapeNet是一个丰富标注的大规模点云数据集 xff0c 其中包含了55中常见的物品类别和513000个三维模型 2 ShapeNetSem 这是一个小的数据
  • git push代码到远程新分支

    Git push 获取远程代码修改后 想要push到远端与原来不同的新分支 xff0c 可以使用下面的命令实现 xff1a git push origin 本地分支 远端希望创建的分支 例如git下来的分支为master span clas
  • 【numpy求和】numpy.sum()求和

    numpy sum a axis 61 None dtype 61 None out 61 None keepdims 61 initial 61 source 用于计算array元素的和 python中常用的numpy进行数学计算 xff
  • Nuttx romfs与启动脚本rcS

    ARM系统上电后 xff0c 系统将flash地址映射到零地址处 xff0c 处理器从零地址处开始运行第一条指令 而在零地址处 xff0c 一般是系统复位中断向量 xff0c 此处存放的是一条跳转指指令 xff0c 通过该条换指令 xff0
  • 在终端/命令行下打开文件浏览器窗口--Win cmd &Ubuntu terminal

    在命令行下想要可视化查看文件 xff0c 可以使用命令直接打开图形化窗口 1 Windows windows上可以使用explorer exe打开资源管理器 xff1a explorer exe span class token keywo
  • 127.0.0.0与0.0.0.0的区别

    1 IP地址分类 ref https tools ietf org html rfc1700 page 3 IP地址表示 IP地址由两个部分组成 xff0c net id和host id xff0c 即网络号和主机号 net id 表示ip
  • 【python】代码换行的几种方法

    代码太长怎么办 xff0c 反斜杠 引号 34 34 34 39 来帮忙 xff01 在写list或者较长的字符串时候 xff0c 或者多个循环造成IDE不够用时 xff0c 就需要代码换行了 主要的代码换行有通用的反斜杠 和针对字符串起作
  • 自己动手写C++迭代器

    综述 关于STL iterator和 iterator adapter 的部分我已在先前的博客 stl源码剖析笔记之iterator 中有所提及 xff0c 下面我们可以试着自己动手写一个简单的迭代器工具 step iterator xff