谈一谈stl的iterator

STL容器迭代器自增自减问题总结
本文讲述使用std::map的iterator时遇到问题,总结了vc和stlport中list、vector、map/set等容器iterator自增自减的行为。如list是循环队列,vector反引用会违规访问,map/set在不同实现中有差异。还强调做事不能有侥幸心理,要提前检查可能出错处。

这两天,使用std::map的iterator的时候,遇到了很多的问题,最后发现很多想当然的想法是错误的。
比如:list的iterator自减到了begin()之后,再自减的话会回到end(),从而形成一个循环队列。
而map的iterator如果一直自减到begin()之后,继续自减的话,iterator会始终指向begin();end()之后自增的话,也不会有任何改变。
其实上述行为还与stl的实现有关,比如stlport的map,end()之后自增,iterator不会始终指向end()。所以我们可以只能认为iterator在begin()之后--、或者end()之后++,这两种行为的效果是未定义的,使用的时候必须非常小心。

以下,针对vc和stlport的stl实现,就各种容器的iterator作一个总结:
1. list: vc和stlport的实现是一致的,即循环队列。begin()-- = end(),end()++ = begin()。list::iterator实际上存储的是指向元素的指针。
2. vector: 在vc和stlport中,vector都使用整数作为iterator,所以begin()--,end()++如果反引用的话都是对内存的违规访问。
3. map/set: map和set本质上都是使用一个treebase的结构。实际上set就是只有key的map。如前所述vc中begin()--,以及end()++都不会有任何操作。但是stlport中确对end()++有特殊处理(注意以下代码中的check special case),由于暂时还没没有研究map中使用的树的操作方式(这得复习数据结构的知识了,以后有空再说吧),所以对下面这段代码的意思还没搞清楚。不过从效果上来看,end()++是变化的。

template <class _Dummy> _Rb_tree_node_base* _STLP_CALL
_Rb_global<_Dummy>::_M_increment(_Rb_tree_node_base* _M_node) {
  if (_M_node->_M_right != 0) {
    _M_node = _Rb_tree_node_base::_S_minimum(_M_node->_M_right);
  }
  else {
    _Base_ptr __y = _M_node->_M_parent;
    while (_M_node == __y->_M_right) {
      _M_node = __y;
      __y = __y->_M_parent;
    }
    // check special case: This is necessary if _M_node is the
    // _M_head and the tree contains only a single node __y. In
    // that case parent, left and right all point to __y!
    if (_M_node->_M_right != __y)
      _M_node = __y;
  }
  return _M_node;
}

总结一下:做任何事情,动手之前都必须深思熟虑。对于可能存在错误的地方,必须检查清楚,否则等到代码写完之后再回头来检查,那将是非常困难的事情。
这两天使用map时,写代码的时候已经意识到iterator的begin()--和end()++可能有问题,但是当时由于急于求成,没有停下来好好研究一下iterator,而是想着等出了问题再回头来研究(这时实际上抱着一种侥幸心理,如果没出问题的话,那就不用花时间研究iterator了)。结果果然出了问题,而出了问题的时候早已把begin()--和end()++忘掉了,这时候必须通过设断点,单步运行等手段一步一步检查可能出现的错误,花了半天才想起begin()--的问题。由此看来做事千万不要有侥幸心理,否则因小失大、得不偿失!

### C++ STL 迭代器的使用方法与实例 #### 定义与基本概念 迭代器是一种用于遍历容器内元素的对象,类似于指针。通过迭代器可以访问和操作标准模板库(STL)容器中的各个元素[^1]。 #### 创建与初始化 对于大多数STL容器而言,在定义对象的同时会自动创建相应的迭代器类型。例如`std::vector<int>::iterator`表示指向整数向量元素的迭代器。下面展示了一个简单的例子: ```cpp #include <iostream> #include <vector> int main(){ std::vector<int> vec = {1, 2, 3}; // 获取起始位置和结束位置的迭代器 auto start_it = vec.begin(); auto end_it = vec.end(); } ``` 这里展示了如何获取一个向量的第一个元素(`begin`)以及最后一个有效元素之后的位置(`end`)对应的迭代器。 #### 遍历容器 利用循环语句配合迭代器能够轻松实现对整个序列的操作。如下所示为打印所有元素的过程: ```cpp for(auto iter=vec.begin();iter!=vec.end();++iter){ std::cout << *iter << ' '; } // 输出结果:1 2 3 ``` 这段代码片段实现了从头到尾逐一遍历并输出每个数值。 #### 查找特定值 除了顺序扫描外,还可以借助于某些高效算法完成更复杂的任务,比如查找指定项。这正是前面提到过的 `std::find()` 函数的作用之一: ```cpp auto pos = std::find(vec.begin(), vec.end(), target_value); if(pos != vec.end()){ std::cout << "Found value at position:" << distance(vec.begin(),pos)<<'\n'; }else{ std::cout<<"Value not present\n"; } ``` 此段程序尝试定位目标值得索引;如果找到则返回其下标,反之给出提示信息。 #### 修改元素 当需要改变某个具体位置上的内容时,可以通过解引用运算符(*)来达到目的: ```cpp *start_it = new_val; ``` 上述命令将会把第一个元素更新成新的给定值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值