文章目录

  • ​​1.迭代器​​
  • ​​2.迭代器类型​​
  • ​​3.迭代器源码剖析​​
  • ​​4.反向迭代器​​
  • ​​4.容器常用的成员​​

1.迭代器

  • 迭代器是泛型指针
    (1)普通指针可以指向内存中的一个地址
    (2)迭代器可以指向容器中的一个位置,本质上就是内存地址
    通过重载一些指针相关的运算符,*,指针运算符->,++,–等运算符来支持普通的指针操作,所以迭代器可以看成是一个泛型指针
  • STL的每一个容器类模板中,都定义了一组对应的迭代器类。
    使用迭代器,算法函数可以访问容器中指定位置的元素,而无需关心元素的具体类型。
  • eg:

    begin()指向第一个元素的位置,end()指向最后一个元素的下一个位置

2.迭代器类型

  • 输入迭代器
    可以用来从序列中读取数据
    支持*操作(访问迭代器中所指向位置的元素),++操作,eg:不支持*p=0
  • 输出迭代器
    允许向序列中写入数据
    支持*操作,++操作,eg:支持*p=0(向迭代器所指向的位置写入数据)
  • 前向迭代器
    既是输入迭代器又是输出迭代器,并且可以对序列进行单向的遍历(只支持++操作,不支持–操作)
  • 双向迭代器
    与前向迭代器相似,但是在两个方向上都可以对数据遍历(支持++,–操作)
  • 随机迭代器
    也是双向迭代器,但能够在序列中的任意两个位置之间进行跳转(支持++,–操作,支持+5,-3,+=,-=操作)
  • 迭代器的类型表
  • (P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_运算符

3.迭代器源码剖析

  • eg:P77\01.cpp
#include <vector>
#include <iostream>

using namespace std;

int main(void)
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);

vector<int>::iterator it;//设置断点
for(it=v.begin(); it!=v.end();++it)
{
cout<<*it<<' ';
}

cout<<endl;

return 0;
}
  • 断点位置
vector<int>::iterator it;//设置断点
  • iterator就是一个模板类_Vector_iterator
  • (P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_开发语言_02


  • (P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_开发语言_03

  • 迭代器的构造函数
    vector中的迭代器类,实际上只有一个数据成员int*,相当于对int*进行包装,迭代器内部持有int*类型;
    _Vector_iterator常量迭代器继承至_Vector_const_iterator常量迭代器,只是不能更改而已;
  • (P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_容器_04


  • (P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_c++_05

  • F11,调用基类的构造函数
  • (P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_开发语言_06

  • _Vector_const_iterator与普通iterator的区别是啥?
    _Vector_const_iterator中pointer是_Alloc中的const_pointer,实际上传递进来的类是int类型,所以pointer的类型是int*类型,const_reference实际上就是const int &,
    _Vector_const_iterator迭代器就是对指针的一种包装,是泛型指针,提供了一些指针相关的运算符来支持普通指针的一些操作。
  • (P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_迭代器_07

  • _Vector_const_iterator里面有一个数据成员_Myptr
  • (P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_迭代器_08

  • _Tptr是如下,对于当前来讲就是int*
  • (P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_c++_09

  • 断点位置:
for(it=v.begin(); it!=v.end();++it)
  • v.begin()实际上就是将第一个元素的指针位置赋值给迭代器当中的指针类型
  • (P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_迭代器_10

  • go进去,调用了迭代器实际类型的构造函数,iterator实际类型是Vector_iterator,接着会调用基类的构造函数_Mybase
  • (P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_迭代器_11

  • _Mybase就是_Vector_const_iterator
  • (P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_迭代器_12

  • 接着F11,调用基类的构造函数。
    _Pvector就是this指针,指向向量对象v
  • (P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_运算符_13

  • 接着调用Adopt函数(派生类没有实现Adopt方法,就会调用基类的Adopt方法)
    _Vector_const_iterator继承至Ranit(表明他是一个随机迭代器),随机迭代器持有的类型_Ty,_Alloc::difference_type(表示两个元素之间差的类型,可正可负),const_pointer指针类型,const_conference引用类型。
  • (P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_迭代器_14

  • go进去,看下_Ranint是什么样的类,这种类没有什么有效代码,这种类仅仅是起到标识的作用;
    但是它又继承至_Iterator_with_base
  • (P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_迭代器_15

  • 继续go,_Iterator_with_base也是一个标识类,但是它又继承至_Base_class
  • (P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_容器_16

  • 继续go,
  • (P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_运算符_17

  • 在这个类中提供了_Adopt方法,作用将容器类型_Parent保存到_Mycount中
  • (P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_容器_18


  • (P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_开发语言_19

  • 最后,_Ptr初始化_Myptr,迭代器内部持有的一个指针类型,对于vector来讲,就是持有了int*类型
  • (P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_c++_20

  • 构造一个end()迭代器,将_Mylast传递进去,将_Mylast指针放到iterator当中的一个数据成员当中。
  • (P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_开发语言_21

  • 断点位置:
it!=v.end();
  • 调用了迭代器it的!=号运算符,调用了==号运算符
    !=是_Vector_const_iterator中的不等号运算符

    判断迭代器所持有的指针是否等于传递进来的对象的指针。若相等,则说明这俩迭代器的是一样。
  • 断点位置:
cout<<*it<<' ';

这里的this指针是iterator类型的指针,this是iterator *,_Mybase *就是_Vector_const_iterator *,

再说这里2个**问题,取后面的第一个*,得到的是_Vector_const_iterator ,再取前一个*,就是调用了_Vector_const_iterator 中的*号运算符

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_开发语言_22


(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_容器_23


继续F11,继续到_Vector_const_iterator 中的*号运算符

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_迭代器_24


实际上这个*号操作就是返回_Myptr取*号,对于当前来说,就是int*然后取*号,就是int类型,就是元素值;

这里的reference就是int类型的引用,返回的是一个引用

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_c++_25

  • 继续F11,位置
for(it=v.begin(); it!=v.end();++it)
主要研究++it操作

这里是前置++操作,而后置++操作会多一项临时对象的构造(注意返回的是对象,而不是引用,这里的++会调用前置的++),所以前置效率高一些;

(_Mybase *)this是_Vector_const_iterator *,

取*后,就是_Vector_const_iterator

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_容器_26


继续F11,可以看到,调用的是_Vector_const_iterator 的++操作,最后返回的是对象自身的引用

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_容器_27

  • 总结:
    iterator实际上借助const_iterator来实现,用继承方式来实现,所以不能将其看成是适配器。
    适配器指的是通过模板作为类型参数传递的方式来实现的,eg:反向迭代器reverse_iterator

4.反向迭代器

  • 反向迭代器是以适配器的方式来实现的,说明可以将正向迭代器传递进去来实现,使用正向迭代器来实现反向迭代器的相关功能
  • eg:P77\02.cpp
#include <vector>
#include <iostream>

using namespace std;

int main(void)
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);

vector<int>::iterator it;//设置断点
for(it=v.begin(); it!=v.end();++it)
{
cout<<*it<<' ';
}

cout<<endl;
vector<int>::reverse_iterator ri;
for(ri=v.rbegin(); ri!=v.rend(); ++ri)
{
cout<<*ri<<' ';
}
cout<<endl;
return 0;
}

与正向迭代器的begin,end的区别与联系

rend()指向第一个元素的前一个位置

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_开发语言_28

  • 断点位置
    利用正向迭代器来实现反向迭代器,反向迭代器持有一个正向迭代器的成员
vector<int>::reverse_iterator ri;

构造一个反向迭代器

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_c++_29


reverse_iterator 的类型是typedef,通过将iterator传递到reverse_iterator 适配器中

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_运算符_30


(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_c++_31


reverse_iterator 又继承至反向随机迭代器_Revranit

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_开发语言_32


继续F11,会调用其基类的构造函数

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_容器_33


它持有了一个正向迭代器的成员,要构造_Revranit,先要构造其对象成员,所以会调用正向迭代器的构造函数,即_Vector_iterator

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_迭代器_34


继续F11,会调用_Vector_iterator的构造函数

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_开发语言_35


继续F11,_Vector_iterator继承至_Vector_const_iterator,所以会进入到_Vector_const_iterator构造函数中

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_运算符_36

  • 断点:
    利用正向迭代器的end()来实现反向迭代器rbegin;
    rbegin()是end()之前的一个位置;
for(ri=v.rbegin(); ri!=v.rend(); ++ri)

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_开发语言_37

F11,调用基类

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_容器_38


F11,继续调用基类,将正向迭代器传递给current

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_容器_39


rend()不再跟踪

  • 断点
    看一下!=运算符
for(ri=v.rbegin(); ri!=v.rend(); ++ri)

!=运算符重载了函数模板

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_迭代器_40


F11

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_迭代器_41


F11,调用_Equal方法

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_运算符_42


F11,返回current

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_容器_43


继续F11,会调用_Vector_const_iterator中的==运算符,因为是调用了正向迭代器来实现的

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_c++_44


继续F11

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_迭代器_45


继续F11

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_迭代器_46

  • 断点:
    rbegin()借助end()来实现,指向前一个;
    rend()借助begin()来实现,也指向前一个;
    关键在于*运算符的重载;
cout<<*ri<<' ';

首先保存正向迭代器current(因为所借助的正向迭代器本身是不能改变的),然后–,再做一个取*操作

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_迭代器_47


F11,–操作借助正向迭代器来实现

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_迭代器_48


_Vector_iterator常量迭代器继承至_Vector_const_iterator常量迭代器,(_Mybase *)this所以先转为_Vector_const_iterator *,然后取*,就是_Vector_const_iterator ,–就是调用_Vector_const_iterator 的–运算符

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_运算符_49


(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_容器_50


继续F11,

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_c++_51


(_Mybase *) this所以先转为_Vector_const_iterator *,*(_Mybase *)this就是_Vector_const_iterator ,**(_Mybase *)this就是调用_Vector_const_iterator 中的*运算符

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_c++_52


F11,所以最终调用的是_Vector_const_iterator 来实现的

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_c++_53

  • 迭代器的实现原理总结:
    反向迭代器以适配器的方式来实现,借助正向迭代器来实现;而普通正向迭代器又借助_Vector_const_iterator 来实现,实际上就是重载指针运算符,++运算符来实现的;
    每一种类型都有自己的迭代器,每一种类型的迭代器内部实现方式可能不一样,vector仅仅是持有相应元素类型的指针,对指针进行操作;
  • eg:
  • 断点位置:
list<int>::iterator it2;

F11,调用它的构造函数

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_开发语言_54


F11

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_c++_55

  • 断点位置:
for(it2=l.begin(); it2!=l.end();++it2)

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_迭代器_56


F11,链表的结构就是_Nodeptr

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_容器_57


F11

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_迭代器_58


F11

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_开发语言_59


F11,链表最关键的地方就是++

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_运算符_60


F11

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_容器_61


F11,_Nextnode()求出当前节点的下一个节点

(P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_容器_62

  • 总结
    每一种容器的类型都有自己迭代器的实现方式,不同类型,其数据组织方式是不同的。
    所以应该有不同的迭代器,来确定其是如何遍历的。
    链表list由next的方式来遍历,向量vector按照位置来遍历

4.容器常用的成员

  • 容器成员1
  • (P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_运算符_63

  • size_type是无符号整数;
    difference_type是整数;
    iterator,迭代器,就是一个泛型指针,相当于原始类型的指针
    关联式容器内部的数据是自动排序的,需要key_compare
  • 容器成员2
  • (P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_c++_64

  • map,set容器也支持下标[]运算;
    at()较与[],支持测试是否越界;
  • 容器成员3
  • (P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_迭代器_65

  • 容器成员4
  • (P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_开发语言_66

  • 容器成员5
  • (P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_c++_67

  • 容器成员6,关联操作的只适用于关联式容器
  • (P77)stl(五):迭代器,迭代器类型,迭代器源码剖析_迭代器_68

  • 参考:从零开始学C++之STL(三):迭代器类vector::iterator 和 vector::reverse_iterator 的实现、迭代器类型、常用的容器成员