C++标准库学习之 STL 迭代器学习

60 阅读3分钟

迭代器是一个 可遍历 STL 容器全部或部分元素的对象,用来表现容器中某一个位置,

基本操作

  • operator* 返回当前位置的元素,如果当前位置的元素存在,也可以使用 -> 获取他们

  • operator++ 令迭代器前进至下一个元素,大多数迭代器也可以使用 operator-- 前进一个元素

  • operator==或!= 判断两个迭代器是否指向同一个地址,

  • operator= 迭代器赋值

这些操作运用pointer操作寻常的arrya 元素时是保持一致的,差别在于迭代器是所谓的 smart pointer ,具有遍历复杂数据结构的能力,其遍历机制取决于其所遍历的数据结构,因此每一种容器都必须有自己的迭代器

所有的容器类都提供一些基本的成员函数,让我们可以获取到迭代器并以之遍历所有的元素,这些函数重要的是 begin()容器的起点,指向第一个元素的位置,end() 指向容器的终点,他的位置在容器最后一个数据后的下一个位置,如实begin 和 end 就形成了一个半开区间,关系图大致如下

image.png

这种关系有两个优点

1: 为遍历 loop 结束提供一个简单的逻辑判断,只要未到达 end, loop 就可以继续进行

2:不必对空区间采取特殊处理手法,空区间的begin 就等价于 end

下面提供一个简单的例子

int tsm_iterator(){

    list<char> lis;

    for (int i = 'a'; i <='z'; ++i) {
        lis.push_back(i);
    }
    list<char>::const_iterator iterator;

    for (iterator=lis.begin();iterator!=lis.end();++iterator) {
        cout<< *iterator<<endl;
    }
}

结果:

D:\CWorkSpace\tsmTest\cmake-build-debug\tsmTest.exe
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z

Process finished with exit code 0

任何容器都定义了两种迭代器类型

1: container::interator 以读写的模式遍历容器

2: container::const_interator 以只读的模式遍历容器

这里还是list 为例简单的看一下 list 中 的源码

template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
  class list : protected _List_base<_Tp, _Alloc>
  {
      typedef _List_iterator<_Tp>        iterator;
      typedef _List_const_iterator<_Tp>         const_iterator;
  }

那么就意味着,在遍历过程中,如果你获取的是 const_iterator ,那么在整个循环工程中你是无法对 *const_iterator 做更改了,但是如果你获取到了 iterator ,如果存入的数据是一个非 const 的数据,那么在遍历过程中就可以使用 *iterator 对数据做更改,

++iterator vs iterator++

注意 在遍历过程中尽量使用前置式递增,而避免使用后置式递增,因为前者的效率更高,原因是后者必须保存一个临时变量,来返回当前的位置,

所以在使用迭代器的遍历过程中尽量使用 ++iterator ,不要使用 iterator++ ,当然这里的推荐使用++iterator并不是以代码的健壮性和易读性来换取的,只是这种使用方式会带来些许性能上的提升

cbegin() 与 cend()

在C++ 11 开始引入的auto 的概念,他给我们带了非常多的好处,还是前面的list 为例,我们可以使用 auto 来简化代码,如下

int tsm_iterator(){

    list<char> lis;

    for (int i = 'a'; i <='z'; ++i) {
        lis.push_back(i);
    }

    for (auto it=lis.begin();it!=lis.end();++it){
        cout<< *it<<endl;
    }
}

相较于之前的 list::iterator 确实是简化了不少,而且如果在后期数据类型发生改变的情况,这里的代码也是适用的,但是使用 auto 也带来了一定的风险,那就是 auto 会使迭代器丧失常量性, 也就是 auto it=lis.begin()获取的是一个非常量的迭代器,为了确保在整个过程中仍然能使用 常量迭代器,自C++ 11 开始容器提供了常量迭代器 cbegin() 与 cend(),他们返回的类型是 const_iterator

Range-Based for vs 迭代器

在遍历 STL 容器我们还可以使用更为便捷的方法, 还是以上面的list为例 方式如下

for (auto item:lis){
    
}

他被翻译成迭代器的版本如下


for (auto it=lis.begin();it!=lis.end();++it){
    auto item=*it;
}