迭代器特性介绍

c++ | 五种 STL 迭代器 文章目录一、迭代器是什么?二、迭代器类别输入迭代器输出运算符前向迭代器双向迭代器随机访问迭代器三、不同容器所对应的迭代器类型四、迭代器的定义方式五、代码实现 一、迭代器是什么? 迭代器是一种抽象的设计理念,通过迭代器可以在不了解容器内部原理的情况下遍历容器。除此之外,STL 中迭代器一个最重要的作用就是作为容器与 STL 算法的粘结剂,只要容器提供迭代器的接口,同一套算法代码可以利用在完全不同的容器中,这是抽象思想的经典应用。 简单来讲,迭代器和 C++ 的指针非常类似,它可以是需要的任意类型,通过迭代器. 阅读详情

原文请见:http://www.codeproject.com/KB/stl/Iterator_traits.aspx

 

简介

如果去查字典,特性就是特点/属性的意思。C++中也不例外,本文中的迭代器特性,指的就是C++ Iterator的特点/属性。继续深入研究之前,我们需要先基本了解一下C++中迭代器是如何设计的。

 

C++ STL(标准模板库)中,有3个有意义和重要的东西:

1.       容器:用于管理一系列某种特定类型的对象。容器分为两种:顺序容器(vectordequelist)和关联容器(SetMultisetMapMultiMap)

2.       算法:用于处理一系列的元素(译注:通常是容器中的元素)。就是说,算法将容器中的元素,按照事先定义好的方法进行处理,并把结果放入同一或另外一个容器中。

3.       迭代器:用于遍历对象聚集(collection)中的各个元素(即总所周知的,容器中的元素。aka = as known as)

 

STL的设计者,采用了奇妙的而且简单通用的方法 - “数据和操作的分离”。

-          数据保存在容器中,并由容器管理它们。

-          对于容器的操作是由可配置的算法来定义和管理的。

 

因此,如果容器类和算法分别为完全不同的实体,那么他们之间必然存在一种桥梁(bridge),对吧?他们之间必须有专门的通道,必须有某种粘合剂(glue)将它们关联起来。是的,你对了,迭代器就是这样的桥梁/通道/粘合剂。

 

或许有人会说STL中的容器/算法/迭代器的概念和面向对象编程的基本理念相矛盾:STL分离了数据和操作(算法),而不是将他们融为一体(译注:面向对象的方法,比如在C++中,通常是将成员变量(即数据)和成员函数(即操作)放在同一个类中)。这种设计的优美和灵活之处就是,你可以几乎可以将任何一种容器和任何一类算法进行绑定(任何一类算法,我指的是 – Modifying/Non-Modifying/Mutating/Sorting/Numeric)

 

背景

简单介绍完了,为了写能体现迭代器特性的代码,一些背景知识尚需推敲一下。正如大家知道的,迭代器有不同的分类(随机存取的,双向的等等)。不同分类的迭代器有不同的功能。

 

尽管如此,有时候仍然必要或者必须以无缝的方式,重载迭代器的功能/行为,你可以就此打住我并且问等等,你在说些什么?“你要以迭代器独立方式重载迭代器行为?这到底是什么意思?”

 

是的,你并没有看错 有时候在公共平台上,重载迭代器行为是绝对有必要的,这样外部看起来就像是无缝的一样。

 

哦,你需要例子?例子有很多,下面我给出一些吧:

         1. difference_type count(Iterator iter1, Iterator iter2)

         2. difference_type count_if(Iterator iterq, Iterator iter2, UnaryPredicate op)

在语句1中,count算法的作用,是对容器中的某种特定取值的元素进行计数;在语句2中,count算法在语句1的基础上,元素还必须满足一个一元判定式(Unary Predicate,译注:Predicate可译为谓词、断言或者判定式)…自然就产生一个问题,返回的数据类型是什么?第一种情况可能是可能是一个INT/SHORT。但是no!那不是泛型编程。我们将很快找到答案。

 

另外一点就是:你应该写你自己的泛型算法(这里指cout/count_if),以便用户可以针对任何容器(当然也针对任何迭代器)来调用它。因此,你需要以泛型函数的方式来写,以便可无缝地适用于任何迭代器。

 

另外一个例子是distance()方法。该方法有两个Iterator位置参数,返回他们之间的数值距离(numeric distance)。这个方法也适用于所有的容器和迭代器。

 

还有很多详情参见STL算法相关章节。

 

现在大问题来了,我们如何才能写出无缝的泛型函数/算法呢?

 

多谢STL的设计者们:他们为我们提供了一切。他们提供的一些东西颇有机巧,但是还是很简单并且有效的。

 

首先:每一迭代器类型,C++库提供了一个<iterator tag>,可以用作迭代器的标签。结构看起来如下:(可以在stl_iterator_base_types.hpp文件找到,我用的是MinGw)

//@{

  /**

   *  @defgroup iterator_tags Iterator Tags

   *  这些是空类型,用来区分不同的迭代器。

   *  区别不是因为他们包含了什么,而是,简单地,它们是什么。

   *  不同的算法实现,可用于由不同类型的迭代器支持的不同算法。

  */

  ///  标记输入迭代器

  struct input_iterator_tag {};

  ///  标记输出迭代器

  struct output_iterator_tag {};

  ///  前向迭代器支持输入迭代器操作的超集(译注:原话如此,在OO中,派生类可以说成基类的‘超集’)

  struct forward_iterator_tag : public input_iterator_tag {};

  ///  双向迭代器支持前向迭代器操作的超集

  struct bidirectional_iterator_tag : public forward_iterator_tag {};

  ///  随机存取迭代器支持双向迭代器操作的超集

  struct random_access_iterator_tag : public bidirectional_iterator_tag {};

  //@}

 

注意这里使用了继承。作为例子,任何前向迭代器都是一种输入迭代器。不过要注意,前向迭代器标记仅从输入迭代器标记继承而来,并没有从输出迭代器标记继承什么。因此,前向迭代器肯定不是输出迭代器。实际上,前向迭代器并没有类似输出迭代器那样的需求。

 

如果编写泛型代码,你可能不仅仅对迭代器分类感兴趣。比如,你可能需要知道迭代器所指元素的数据类型。因此,C++标准库提供了一个特殊的模板结构来定义迭代器特性。这个结构包含了和迭代器相关的所有信息,用作任何一个迭代器应该具备的所有类型定义的公共接口(类别、元素数据类型等等)

 

    namespace std {

       template <class T>

       struct iterator_traits {

           typedef typename T::value_type                value_type;

           typedef typename T::difference_type         difference_type;

           typedef typename T::iterator_category        iterator_category;

           typedef typename T::pointer               pointer;

           typedef typename T::reference                      reference;

       };

   }

在这个模板中,T代表迭代器数据类型。这样,你就能够使用这个种类,即元素的数据类型为T的任何迭代器来编写代码,等等。例如,下面的表达式得出了迭代器数据类型T的值类型:

         typename std::iterator_traits<T>::value_type

这个结构两两个有点:

1.       确保一个迭代器提供了所有数据类型定义。

2.       可以针对一些特殊的迭代器,被(部分)特化。后者通过也可用作迭代器的普通指针完成的。

   namespace std {

       template <class T>

       struct iterator_traits<T*> {

           typedef T                          value_type;

           typedef ptrdiff_t                  difference_type;

           typedef random_access_iterator_tag iterator_category;

           typedef T*                         pointer;

           typedef T&                         reference;

       };

   }

因此,对于任何指向”T”的数据类型指针,它被定义为随机访问迭代器这个分类。对于常量指针(const T*),存在一个对应的部分特化。

 

使用代码

为迭代器写泛型函数

使用迭代器特性,可以编写派生自数据类型定义或根据迭代器分类不同的实现代码的泛型函数。

 

使用迭代器分类

使用为不同迭代器分类而编写的不同实现,你必须准手这两步:

1. 让你的模板函数,将迭代器分类作为一个附加参数来调用另外一个函数,例如:

template <typename Itr>

inline void my_function(Itr begin, Itr end)

{

    my_function (beg, end,

        std::iterator_traits<Itr>::iterator_category());

}

 

2. 为所有迭代器实现其他函数,以提供一个不继承其他任何迭代器分类的特殊实现。例如:

// 双向迭代器的my_function()

  template <typename BidectionalIterator>

  void my_function (BidectionalIterator begin, BidirectionalIterator end,

                    std::bidirectional_iterator_tag)

  {

        // 双向迭代器相关代码

  }

 

 // 随机存取迭代器的my_function()

  template <typename RandomIterator>

  void  my_function(RandomIterator begin, RandomIterator end,

                    std::random_access_iterator_tag)

  {

        // 随机存取迭代器相关代码

  }

 

distance()的实现

本文前面提到的一个例子,就是实现迭代器辅助函数distance()。该函数返回两个迭代器位置之间的距离和元素。随机存取迭代器的实现仅使用操作符 – (即一元减号)。所有其他分类的迭代器,还要返回到达范围结尾所需增加的数字。

请仔细阅读下面代码中的注释。

// 用户总是调用这个方法,distance()的通用方法

// 必须满足下面两个条件:

// 对于随机访问迭代器,第二个迭代器位置必须出现在第一个迭代器位置之后;

// 对于其他所有分类的迭代器,第二个迭代器必须可以从第一个迭代器到达,两种迭代器必须指向同一个// 容器,否则结果是未定义的

    template <typename Iterator>

    typename std::iterator_traits<Iterator>::difference_type

    distance (Iterator pos1, Iterator pos2)

    {

        return distance (pos1, pos2,

                         std::iterator_traits<Iterator>

                            ::iterator_category());

    }

 

    // 核心实现….随机存取迭代器的distance()

    template <typename RandomAccessIterator>

    typename std::iterator_traits<RandomAccessIterator>::difference_type

    // 注意上面的返回类型,完全依赖与迭代器中声明的类型

    // 此处并非所谓的INT/SHORT

    distance (RandomAccessIterator first_position, RandomAccessIterator second_position,

               std::random_access_iterator_tag)

    {

        return second_position - first_position;

    }

 

    // 输入、前向和双向迭代器的distance()

    // 留意这里我们仅用了输入迭代器标记,所以

    // 前向和双向迭代器也在这个方案中,因为我们

    // 在声明前向和双向迭代器标记时,使用了继承

    template <typename InputIterator>

    typename std::iterator_traits<lnputIterator>::difference_type

    // 注意这里的返回数据类型,真正的泛型

    distance (Inputlterator first_position, InputIterator second_position,

              std::input_iterator_tag)

    {

        // 注意这里临时变量的数据类型,真正的泛型。

        typename std::iterator_traits<lnputIterator>::difference_type diff;

        for (diff=0; first_position != second_position; ++first_position, ++diff) {

             ;

        }

        return diff;

    }

不同的迭代器数据类型可用作返回类型。注意第二个版本使用了输入迭代器标记,因此这个实现也将被用于前向和双向迭代器,因为它们的标记是从input_iterator_tag继承而来。

 

我们可以继承同样的逻辑,为count/count_if定义返回数据类型。因此理想的泛型返回数据类型应该是:

template <typename iterator>

typename std::iterator_traits<iterator>::difference_type

count(iterator itr1, iterator itr2){

// 你的逻辑

}

 

进一步实现

应用这种方式,你可以写出许多泛型方法。这种方法的确必要,在你碰到需要一个可以和任何种类的迭代器无缝协作的泛型函数时,尤其如此。

 

参考资料

C++编程语言 作者Stroustrup

C++标准库:教程和参考,作者Josutis

 

历史

2009518,初次发表

NVIDIA Jetson之交叉编译工具 参考:http://bbs.xfyun.cn/forum.php?mod=viewthread&amp;tid=33630 用TX2做红外热成像仪的识别,厂家不提供源码,只能提供对应版本的SDK,但是需要我们自己提供交叉编译器 由于之前没有做过交叉编译相关工作,对编译工具不太了解,因此查找相关资料学习下 交叉编译知识 一般来说,针对嵌入式平台的开发有两种编译模式:原生编译和交叉编译。原生编... 阅读详情

相关推荐

基于STM32L4XX 、HAL库的DRV8833RTYR步进电机驱动器驱动程序应用设计

/ 注:具体计算取决于你的定时器配置,例如 Period=1000, duty_cycle 0-100 -> compare_value = duty_cycle * 10。// 控制电机方向/停止/刹车。//================== 硬件引脚映射 (请根据实际电路修改) ==================

PP_Cottage的博客 162

迭代器概述

1、迭代器概述 迭代器的特点: 用于指向顺序容器和关联容器中的元素 迭代器用法和指针类似 有const 和非 const两种 通过迭代器可以读取它指向的元素 通过非const迭代器还能修改其指向的元素 2、定义和使用迭代器 (1)定义迭代器 //定义非const迭代器 容器类名::iterator 变量名; //定义const迭代器 容器类名::const_iterator 变量名; (...

江南又一春 508

安卓版本web服务器 almp_v7.0ALMP

安卓服务器,本人亲测可用,对系统资源占用较多(200mb左右吧),可以做个小型的服务器,支持php、asp、mysql。

【C++ STL应用与实现】19: 迭代器特性-iterator traits

本文介绍了STL中的迭代器相关的类型和特性,它们用来定义和区分不同的迭代器类型。如iterator tag作为迭代器的“标签”用来区分迭代器的类型;iterator traits定义了所有类型的迭代器都应该有的公共信息。那标准库为什么提供这些东西呢?答案是我们可以根据这些信息来编写泛型代码,在泛型代码里根据iterator traits来判定迭代器的类型以做相应处理、可以自己定义迭代器实现自定义的迭代器操作。

Small Flows 2881

oracle 迭代器,迭代器

迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。一、NEXT语法:next(iterator[, default])说明:1、函数必须接收一个可迭代对象参数,每次调用的时候,返回可迭代对象的下一个元素。如果所有元素均已经返回过,则抛出StopIteration 异常。>>> a = iter('abc...

weixin_39882394的博客 415

C++STL Iterator和traits

STL Iterator和traits STLIterator 1.Iterator定义 STL的中心思想在于将数据容器和算法分开,彼此独立设计,然后再用一个粘合剂将其黏再一起,这个粘合剂就是iterator迭代器是一种智能指针,它针对指针中最常见的内容提领(dereference)和成员访问(member access),这里隐含的一个重要的消息就是迭代器必须包含原生指针,即接待器是原生指针的一种拓展。所以迭代器最重要的就是重载operator*和operator->。 2.实现.

b711183612的专栏 1090

[C++] 分析std::iterator和std::iterator_traits

std::iterator和std::iterator_traits std::iterator std::iterator是一个模板类,其声明为: template&lt; class Category, class T, class Distance = std::ptrdiff_t, class Pointer = T*, clas...

DLililiDengDaa~~ 4567

Python学习(五)高级特性(切片、迭代、列表生成式、生成器、迭代器)、模块介绍

Python学习(五)高级特性(切片、迭代、列表生成式、生成器、迭代器)、模块介绍

ACGkaka的博客 1770

ES6语法特性(二)迭代器 生成器 Promise介绍

自定义遍历obj的list数据,而非objreturn {index++;

qq_36158551的博客 554

Go1.23 新特性:争议最大的 iter 迭代器,可遍历万物!

大家好,我是煎鱼。Go1.23 新版本中,在发布过程中争议最大的新特性莫过于:迭代器iterators)。原本计划先写一个这个 proposal 的提出背景的,但没想到,迭代器涉及的到 proposal 比较多,而且是由 rsc 亲自负责。总感觉 rsc 早有预谋,在 Go1.23 蓄力一击,搞完就撤了。Go1.23 新特性迭代器提出过程我能翻到的最早明确提出要加迭代器是在 discussio...

EDDYCJY的博客 1978

golang中的迭代器和 iter 包

golang中的迭代器和 iter 包

猛犸象 2079

C++基础:迭代器

模板使算法独立于存储的数据类型,迭代器使算法独立于使用的容器类型。输入迭代器必须能够访问容器中所有的值。这是通过支持++运算符来实现的。基于输入迭代器的任何算法都应当是单通行的(single-pass,可以递增,不能倒退)(不依赖前一次遍历时的迭代器值,也不依赖本次遍历中前面的迭代器值)程序的输出就是容器的输入。只能解除引用来修改容器值,而不能读取。(cout就是可以修改发送到显示器的字符流,而布恩那个读取屏幕上的内容)(适用于单通行只写算法)只能使用++运算符来遍历容器,与上述两个迭代器不同的是,它总是按

FranYeCisco的博客 399

2 STL迭代器介绍【前向迭代器、双向迭代器、随机访问迭代器】【迭代器遍历容器】

文章目录0 - 前言1 - 迭代器定义2 - 迭代器分类3 - 迭代器使用 0 - 前言 参考:http://c.biancheng.net/view/6675.html 1 - 迭代器定义 每一种容器都有对应类型的迭代器,也就是,不同容器的迭代器也不同,其功能强弱也有所不同。容器的迭代器的功能强弱,决定了该容器是否支持 STL 中的某种算法。 2 - 迭代器分类 常用的迭代器按功能强弱分为输入迭代器、输出迭代器、前向迭代器、双向迭代器、随机访问迭代器 5 种。常用的就是后三种,输入迭代器和输出迭代器

日月既往,不可复追 6074

迭代器特性

C++标准程序库为每一种迭代器提供了一个

Ran 4964

【C++ STL迭代器】详细介绍

C++ 允许用户定义自己的迭代器,通过实现迭代器接口的相关函数,可以将自定义数据结构与 STL 算法结合使用。

YRB0127的博客 2787

【C++】迭代器、反向迭代器详解

http://c.biancheng.net/view/338.html https://blog.csdn.net/kjing/article/details/6936325 https://blog.csdn.net/qq_22194315/article/details/57144854 一、迭代器 1. 概述 要访问顺序容器和关联容器中的元素,需要通过“迭代器iterator)”进行。迭代器是一个变量,相当于容器和操纵容器的算法之间的中介。迭代器可以指向容器中的某个元素,通过迭代器就可以读..

lemonxiaoxiao的博客 6001

STL之迭代器的类型介绍

STL 主要是由 containers(容器),iterators(迭代器)和 algorithms(算法)的 templates(模板)构成的.        对应于它们所支持的操作,共有五种 iterators(迭代器)。            input         output               \            /

startwithdp的专栏 1359

迭代器用法详解

迭代器实际上是一种用来遍历容器元素的数据类型(作用类似与C语言的指针)。从此就可以看出,迭代器与容器的密切联系。我们知道,一些容器(如vector)可以通过[ ]来访问容器元素,但这种方式并不普遍,因为大多数的容器并不是数组实现的,也就不能用[]来访问容器元素。因此C++中就需要一种更加普遍的工具来提高效率,这就是迭代器迭代器是一种数据类型,一种变量。它也有几种类型,输入迭代器,输出迭代器,前向迭代器,双向迭代器,随机迭代器。所以它支持++,--,>,=,

2301_78933228的博客 2947

【C++】初谈迭代器

迭代器是一种可以访问和遍历容器中元素的对象,它类似于指针,但是具有更多的功能和灵活性。本文将介绍C++迭代器的基本概念、分类、用法和注意事项。

心之王者的博客 2290
上一篇: WinPcap教程(3):打开网卡抓包
下一篇: 程序员的credit
pathuang68
博客等级 码龄18年 1821粉丝 194原创
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值