• C++之STL迭代器


    迭代器是一种检查容器内元素并遍历元素的数据类型。可以替代下标访问vector对象的元素。

    每种容器类型都定义了自己的迭代器类型,如 vector:

    1. vector<int>::iterator iter;  

    这符语句定义了一个名为 iter 的变量,它的数据类型是 vector<int> 定义的 iterator 类型。每个标准库容器类型都定义了一个名为 iterator 的成员,这里的 iterator 与迭代器实际类型的含义相同。

    begin 和 end 操作

    每种容器都定义了一对命名为 begin 和 end 的函数,用于返回迭代器。如果容器中有元素的话,由 begin 返回的迭代器指向第一个元素:
    1. vector<int>::iterator iter = ivec.begin();  
    上述语句把 iter 初始化为由名为 vector 操作返回的值。假设 vector 不空,初始化后,iter 即指该元素为 ivec[0]。由 end 操作返回的迭代器指向 vector 的“末端元素的下一个”。表明它指向了一个不存在的元素。如果 vector 为空,begin 返回的迭代器与 end 返回的迭代器相同。由 end 操作返回的迭代器并不指向 vector 中任何实际的元素,相反,它只是起一个哨兵(sentinel)的作用,表示我们已处理完 vector 中所有元素。
    【备注:不用担心begin和end在循环中的条件判断。大胆使用吧!】

    vector 迭代器的自增和解引用运算

    迭代器类型可使用解引用操作符(dereference operator)(*)来访问迭代器所指向的元素:
     
    1. *iter = 0;  
    解引用操作符返回迭代器当前所指向的元素。假设 iter 指向 vector 对象 ivec 的第一元素,那么 *iter 和 ivec[0] 就是指向同一个元素。上面这个语句的效果就是把这个元素的值赋为 0。
    迭代器使用自增操作符向前移动迭代器指向容器中下一个元素。从逻辑上说,迭代器的自增操作和 int 型对象的自增操作类似。对 int 对象来说,操作结果就是把 int 型值“加 1”,而对迭代器对象则是把容器中的迭代器“向前移动一个位置”。因此,如果 iter 指向第一个元素,则 ++iter 指向第二个元素。
    由于 end 操作返回的迭代器不指向任何元素,因此不能对它进行解引用或自增操作。

    迭代器的其他操作

    另一对可执行于迭代器的操作就是比较:用 == 或 != 操作符来比较两个迭代器,如果两个迭代器对象指向同一个元素,则它们相等,否则就不相等。

    迭代器应用的程序示例

    1、使用迭代器和下标改变vector的内容

    这个很简单,请看代码。
     
    1. #include <iostream>  
    2. #include <string>  
    3. #include <vector>  
    4.   
    5. int print_int_vector(std::vector<int> ivec)  
    6. {  
    7.     for(std::vector<int>::size_type ix =0, j = 0; ix != ivec.size(); ++ix, ++j)  
    8.     {  
    9.         std::cout<<ivec[ix]<<" "; //加空格!  
    10.     }  
    11.     std::cout<<std::endl;  
    12.     return 0;  
    13. }  
    14.   
    15. int main()  
    16. {  
    17.     std::vector<int> ivec(10, 68); // empty vector  
    18.     print_int_vector(ivec);  
    19.     // reset all the elements in ivec to 0  
    20.     /* 
    21.         // 使用下标 
    22.     for (std::vector<int>::size_type ix = 0; ix != ivec.size(); ++ix) 
    23.     { 
    24.         ivec[ix] = 0; 
    25.     } 
    26.     */  
    27.     // equivalent loop using iterators to reset all the elements in ivec to 0  
    28.     for (std::vector<int>::iterator iter = ivec.begin(); iter != ivec.end(); ++iter)  
    29.         *iter = 0; // set element to which iter refers to 0  
    30.     print_int_vector(ivec);  
    31.     return 0;  
    32. }  

    2、tuple功能的实现【不可变性】

    const_iterator类型只能用于读取容器内元素,但不能改变其值。
    当我们对普通 iterator 类型解引用时,得到对某个元素的非 const。而如果我们对 const_iterator 类型解引用时,则可以得到一个指向 const 对象的引用),如同任何常量一样,该对象不能进行重写。
    如果使用const_itreator进行重写,编译时会报错!
    使用 const_iterator 类型时,我们可以得到一个迭代器,它自身的值可以改变,但不能用来改变其所指向的元素的值。可以对迭代器进行自增以及使用解引用操作符来读取值,但不能对该元素赋值。
    【注意:不要把 const_iterator 对象与 const 的 iterator 对象混淆起来。声明一个 const 迭代器时,必须初始化迭代器。一旦被初始化后,就不能改变它的值。】
     
    1. vector<int> nums(10); // nums is nonconst  
    2. const vector<int>::iterator cit = nums.begin();  
    3. *cit = 1; // ok: cit can change its underlying element  
    4. ++cit; // error: can't change the value of cit  
    【注意:const_iterator 对象可以用于 const vector 或非 const vector,因为不能改写元素值。const 迭代器这种类型几乎没什么用处:一旦它被初始化后,只能用它来改写其指向的元素,但不能使它指向任何其他元素。】
    tuple不可变的实现需要使用const声明!const vector<int> nums(10, 9);

    总结

    1、const_iterator需要注意:这个vector本身还是可变的,只不过对const_iterator类型解引用的对象不可变。
    2、const迭代器也就是只能指向其所指向的元素,不能通过++等操作去指向其他元素。但是,所指向这个元素可以改变。
    3、需要定义真正tuple,那就用const vector<int> nums(10, 9);来定义!此时,必须使用const_iterator 来获取每个元素的值。

    迭代器的算术操作

    1、可以对迭代器对象加上或减去一个整形值。这样做将产生一个新的迭代器,其位置在 iter 所指元素之前(加)或之后(减) n 个元素的位置。加或减之后的结果必须指向 iter 所指 vector 中的某个元素,或者是 vector 末端的后一个元素。加上或减去的值的类型应该是 vector 的 size_type 或 difference_type 类型,例子:
     
    1. int main()  
    2. {  
    3.     std::vector<int> ivec(10, 68);   
    4.     print_int_vector(ivec);  
    5.     int i = 0;  
    6.     for (std::vector<int>::iterator iter = ivec.begin(); iter != ivec.end(); ++iter, i++)  
    7.         *iter = i; // set element to which iter refers to i  
    8.     print_int_vector(ivec);  
    9.     std::vector<int>::iterator iter = ivec.begin();  
    10.     iter += 100;  
    11.     std::cout<<*iter;  
    12.   
    13.     return 0;  
    14. }  
    本例子中ivec有10个元素,iter+=j,j在10以内都不会有错(0~9),大于等于10则会出现溢出问题,编译器,运行中间都不会报错!可以加当然可以减。
    2、iter1 - iter2:
    该表达式用来计算两个迭代器对象的距离,该距离是名为 difference_type 的 signed 类型 size_type 的值,这里的 difference_type 是 signed 类型,因为减法运算可能产生负数的结果。该类型可以保证足够大以存储任何两个迭代器对象间的距离。iter1 与 iter2 两者必须都指向同一 vector 中的元素,或者指向 vector 末端之后的下一个元素。
    3、可以用迭代器算术操作来移动迭代器直接指向某个元素,例如,下面语句直接定位于 vector 中间元素:
     
    1. vector<int>::iterator mid = vi.begin() + vi.size() / 2;  
    上述代码用来初始化 mid 使其指向 vi 中最靠近正中间的元素。这种直接计算迭代器的方法,与用迭代器逐个元素自增操作到达中间元素的方法是等价的,但前者的效率要高得多。
    4、任何改变 vector 长度的操作都会使已存在的迭代器失效。例如,在调用 push_back 之后,就不能再信赖指向 vector 的迭代器的值了。
    请看例子:
     
    1. *iter = i; // set element to which iter refers to i  
    2. ivec.push_back(i*2);  
    加上这句代码没问题,正确运行,但是,我们试图在for循环里面执行,即:
     
    1. {  
    2.     *iter = i; // set element to which iter refers to i  
    3.     ivec.push_back(i*2);  
    4. }  
    则会莫名其妙退出!
  • 相关阅读:
    ibatis.net:惯用法
    ibatis.net:在VS中支持xml智能提示
    设计原则:色彩
    编码原则:“防御式编程”再次让我快乐
    错误:update 忘了加 where
    .NET:不要使用扩展方法扩展Object对象。
    错误:不小心的VS重命名
    技术人生:如何更有效率的切换上下文?
    Logstash filter 插件之 grok
    轻松测试 logstash 的配置文件
  • 原文地址:https://www.cnblogs.com/cthon/p/9206262.html
Copyright © 2020-2023  润新知