Iterator学习

参考:https://blog.csdn.net/summerxiachen/article/details/59538640

https://blog.csdn.net/gogokongyin/article/details/51206225

什么是Iterator?

    迭代器本质来说是一个对象,在使用iterator()方法后返回一个Iterator对象,Iterator实际上是一个指针,指向该容器的指针begin位置的指针。但是迭代器和指针是有区别的。

    (1)迭代器本质不是指针,是类模板,表现的像指针。他只是模拟了指针的一些功能, 通过重载了指针的一些操作符,->,*,++ --等封装了指针,是一个“可遍历STL( Standard Template Library)容器内全部或部分元素”的对象, 本质是封装了原生指针,是指针概念的一种提升(lift),提供了比指针更高级的行为,相当于一种智能指针,他可以根据不同类型的数据结构来实现不同的++,--等操作;

    (2)  迭代器返回的是对象引用而不是对象的值,所以cout只能输出迭代器使用*取值后的值而不能直接输出其自身。

    (3)在设计模式中有一种模式叫迭代器模式,简单来说就是提供一种方法,在不需要暴露某个容器的内部表现形式情况下,使之能依次访问该容器中的各个元素,这种设计思维在STL中得到了广泛的应用,是STL的关键所在,通过迭代器,容器和算法可以有机的粘合在一起,只要对算法给予不同的迭代器,就可以对不同容器进行相同的操作。

注:迭代器在使用后就释放了,不能再继续使用,但是指针可以!!

指针:
        指针能指向函数而迭代器不行,迭代器只能指向容器;指针是迭代器的一种。指针只能用于某些特定的容器;迭代器是指针的抽象和泛化。所以,指针满足迭代器的一切要求。
        总之,指针和迭代器是有很大差别的,虽然他们表现的行为相似,但是本质是不一样的!一个是类模板,一个是存放一个家伙的地址的指针变量。    

为什么需要Iterator?

    如果我们来遍历访问容器中的元素,容器也分好多种,比如vector,list等,不同的容器遍历的方法肯定不一样。迭代器的作用就是提供一个遍历的统一接口。那么相对于不同的容器,实现肯定就不一样。这样的好处是在STL设计算法时,就可以脱离容器限制,从而设计出更加通用的算法。比如,在容器中查找一个元素。查找,这个操作一般来说就是遍历整个集合,然后找到那个要找的元素,但是如果没有迭代器,我们就必须要根据不同的容器设计不同的查找算法,比如vector和List就需要设计两种查找算法,因为找下一个元素在vector和list中的操作不通。同样的思想却要两套代码,显然这是不优秀的。

总之,迭代器是为了给所有的容器统一操作接口,迭代器和容器是密不可分的,不同的容器,它的迭代器不同,但是提供的接口是相同的。

详细介绍

    它提供了一种访问一个容器(container)中各个元素的机制。Iterator不会暴露容器对象内部的细节方法。在使用迭代器的时候,我们不需要了解容器底层是如何来实现的,能够对容器进行遍历。因此迭代器也叫做轻量级的容器。

(1)用iterator()方法来建立迭代器,该方法返回的是一个对象。

(2)通过iterator中的next方法[iterator.next()]来得到下一个元素。第一次使用该方法的话就返回第一个元素。

(3)通过iterator中的hasNext()方法[iterator.hasNext()]来判断容器是否为空。

(4)通过Iterator中的remove()方法来删除迭代器返回来的元素。

值得注意的是,Iterator也支持派生,ListIterator该迭代器就只应用在List中,关键是能够在List中双向来操作元素。一般的Iterator是只正向的来遍历集合的。

ConcurrentModificationException异常

出现该异常,通常是由于在使用Iterator遍历容器的时候,对容器进行了删除或增加元素的操作,容器中的元素数目发生了变化。原因是:在用iterator()方法建立Iterator对象的时候,会将此时容器中包含的元素赋值给一个变量叫expectedModCount。在调用next()方法的时候,会比较变量expectedModCount与容器中的实际对象个数是否相等,如果不等,就会抛出该异常。一般会在多线程操作容器的时候出现。

解决办法:把要删除的对象都保存在一个集合中,遍历结束后再整个删除removeAll()或者iterator.remove()方法。

 

 

http://blog.csdn.net/Veahlin/article/details/68490756

这是个困扰我很久了的问题,可能一开始对面向对象的理解不够深。

刚刚想明白了,随手记录一下。


  • 先从const iterator和const_iterator说起

const iterator 是iterator本身是个常量,iterator本身里面存的是指针,也就是iterator的值,也就是那个指针不能改变,也就是不能指向其他的位置,但是所指向的位置的元素是可以通过这个iterator来改变的。

const_iterator 其实本质来说,是另一个类。我们可以想象成,它的数据成员是一个指向常量元素的指针,(比如 const T*)也就是说,这个const_iterator里存着的指针是可以改变的,即可以 ++ 或 - - 操作,但是,这是一个指向常量的指针,指向的元素是常量,不可改变。

  • 有点绕,我们再从一个例子扒一扒。
vector<int> ivec1(10);
const vector<int> ivec2(10);
vector<int>::iterator iter1 = ivec1.begin(); // true
vector<int>::iterator iter2 = ivec2.begin();  // error
vector<int>::const_iterator iter3 = ivec2.begin(); // true
vector<int>::const_iteartor iter4 = ivec1.begin();  // true
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

通过上面这个例子,我们就可以更直观的再深入理解一下了。 
因为ivec2本身就是一个常量的vector,所以ivec2里面的元素必然是不能被改变的。如果直接定义一个iterator是会报错的,因为iterator意味着这个iterator可以遍历容器,且可以改变容器元素,显然如果容器被定义成常量了之后,这个iterator是不合理的。

所以这才有了const_iterator的出现。作为一个迭代器,遍历元素是必须要的,不然就丧失了一个迭代器的意义了。但是由于常量容器的存在,iterator不能满足这个需求。const_iteartor代表的就是不能改变容器元素,但是可以遍历容器的迭代器。

但是const_iterator不仅仅是只针对已经被声明为常量的容器用的,如果一个非常量容器,但是你不想改变容器的元素,那么也可以用const_iterator,因为里面存的是一个指向常量的指针。

  • 最后再来看一下底层实现,更深入的扒一扒。 
    直接看最简单的vector容器吧,list和deque要更加复杂一些,但是本质都是一样的。(list是指向node节点的指针,duque的iterator更加复杂,因为它的内存分配是一段一段的,详情可见STL源码或者我的github www.github.com/linxiaoye/TinySTL)
/*vector的数据结构一般是这样的*/
T* start;
T* finish;
size_t n;

typedef T            value_type;
typedef T*           iterator;
typedef const T*     const_iterator;

iterator begin() { return start; }     // ①
const_iterator begin() const { return start; }  // ②
const_iterator cbegin() const { return start; }  // ③
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

我们可以看到,vector里面是这样实现的。 
begin()有两个实现,外加一个cbegin(),有三个实现。 
①就是最一般的实现,可遍历,可改变元素值。一般用于非常量容器。 
②是可遍历,不可改变元素值,一般用于常量容器。当你对一个常量容器直接调用begin(),就是调用的②。调用值得一提的是,它这个不可改变元素值是全方位表现出来了的。从 typedef const T* const_iterator 开始,就限制了这是一个指向常量的指针,不可改变元素值;②中返回值是const_iterator也说明了返回的是一个存有指向常量的指针的迭代器;cosnt成员函数也说明了,不能通过这个函数来改变元素的值。 
③其实跟②是几乎一样的,只是函数名不同,也就是说,当你实例化一个常量vector时,你调用begin() 和 cbegin()其实是一样的,没有区别。但是,当你定义的是一个非常量容器时,你想调用一个不能改变所指向元素的迭代器,那么就应该调用cbegin(),它可以满足你的要求。

 

  •  
  • 最后总结一下 

 

  • iterator 可遍历,可改变所指元素 
  • const_iterator 可遍历,不可改变所指元素 
  • const iterator 不可遍历,可改变所指元素 
  • const_iterator 主要是在容器被定义成常量、或者非常量容器但不想改变元素值的情况下使用的,而且容器被定义成常量之后,它返回的迭代器只能是const_iterator 

 

  • begin() 与 cbegin() 其实在容器被定义成常量之后,本质上是一样的,没有区别,调用哪个都行;但是容器时非常量时,如果你不想改变元素值,就只能调用cbegin()
  •  

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值