STL迭代器

牧云@^-^@ 提交于 2020-01-07 04:22:33

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

以下内容大多来自《C++标准程序库》

迭代器是一个“可遍历STL容器内全部或部分元素”的对象,一个迭代器用来指出容器中的一个特定位置。

基本操作:

(1)Operator * :返回当前位置上的元素值

(2)Operator ++:将迭代器前进至下一个元素,大多数迭代器还可以使用operator -- 退回到前一个元素

(3)Operator ==和Operator != 判断两个迭代器是否指向同一个位置

(4)Operator = 为迭代器赋值

这些操作与操作数组元素时的指针接口是一致的,不同在于迭代器具有遍历复杂数据结构的能力,其下层运行机制取决于其所遍历的数据结构,因此,每一种容器型别都必须提供自己的迭代器。事实上每一种容器都将其迭代器以嵌套方式定义于内部,因此各种迭代器的接口相同,型别却不相同。

重要函数 

所有容器类别都提供有一些成员函数,使我们得以获得迭代器并一直遍访所有元素。

begin()

返回一个迭代器,指向容器起始点的位置

end()

返回一个迭代器,指向容器结束点。结束点在最后一个元素之后,这样的迭代器又称作逾尾(past-the-end)迭代器(所以遍历的时候都是判断!=end())

#include <iostream>
#include <list>
using namespace std;

int main() {
    list<char>coll;
    for (char c = 'a'; c <= 'z'; ++c) {
        coll.push_back(c);    
    }

/*    while (!coll.empty()) {  //这个也可以显示出来,不过元素都出去了, 所以List空了,程序没有进入迭代器(或者说begin=end了)就结束了
        cout << coll.front() << ' ';
        coll.pop_front();
    }
    */
    list<char>::const_iterator pos;   //迭代器声明于循环之前 (这个不是定义吗?书上写的是声明。。)
    for (pos = coll.begin(); pos != coll.end(); ++pos) {
        cout << *pos << ' ';    //*pos代表当前元素
    }
    cout << endl;
    system("pause");
}

在网上看到了另外一种迭代器写法:

for (auto pos = coll.begin(); pos != coll.end(); ++pos) {  //以后多用前置式递增,它比后置式递增pos++效率要高,而且可能更加符合人们本来的意思
        cout << *pos << ' ';    
    }

这样就可以不用声明了

两种迭代器型别:

container::iterator

这种迭代器以“读/写”模式遍历元素

container::const_iterator

这种迭代器以“只读”模式遍历元素

迭代器分类

1.双向迭代器

该迭代器可以双向行进,以递增运算前进,或以递减运算后腿,list/map/multimap/set/multiset这些容器提供的迭代器都属于此类;

 

2.随机存取迭代器

该迭代器不但具备双向迭代器的所有属性,还具备随机访问能力,可以对迭代器减少或增加一个迁移量、处理迭代器之间的距离或者使用<或>之类的相对关系操作符来比较两个迭代器。vector/deque/strings提供的迭代器都属于此类

比较以下两者

for (auto pos = coll.begin(); pos != coll.end(); ++pos) { 
    cout << *pos << ' '; 
}

for (auto pos = coll.begin(); pos < coll.end(); ++pos) { 
    cout << *pos << ' ';
}

其不同在于测试循环的条件,因为只有随机存取迭代器才支持operator<,所以如果用第二种,则list /set / map无法运作,因此我们最好还是使用第一种迭代方式

匹配器(特殊的迭代器):

1.安插型匹配器(Insert iterators)

Inserters可以使算法以安插的方式而非赋写的方式运作。使用它可以解决目标空间不足的问题,它会促使目标区间的大小俺需要成长。

#include <iostream>
#include <vector>
#include <list>
#include <deque>
#include <set>
#include <algorithm>
#include <iterator>
using namespace std;

int main() {
    list<int>coll1;
    for (int i = 1; i <= 9; ++i) {
        coll1.push_back(i);
    }
    vector<int>coll2;
    copy(coll1.begin(),coll1.end(),back_inserter(coll2));
    for (auto pos = coll2.begin(); pos != coll2.end(); ++pos) {
        cout << *pos << " ";
    }
    cout << endl;
    deque<int>coll3;
    copy(coll1.begin(), coll1.end(), front_inserter(coll3));
    for (auto pos = coll3.begin(); pos != coll3.end(); ++pos) {
        cout << *pos << " ";
    }
    cout << endl;
    set<int>coll4;
    copy(coll1.begin(), coll1.end(), inserter(coll4,coll4.begin()));
    for (auto pos = coll4.begin(); pos != coll4.end(); ++pos) {
        cout << *pos << " ";
    }
    system("pause");
}

结果:

1 2 3 4 5 6 7 8 9
9 8 7 6 5 4 3 2 1
1 2 3 4 5 6 7 8 9

这里刚开始报了一个错误:error C3861: back_inserter : 找不到标识符,查找了一下应该是VC版本更迭出现的问题,需要在头部声明#include <iterator>来解决。

这个例子用了三种预先定义的insert iterators:

1.Back inserters

其内部调用了push_back(),从尾端插入元素(从尾部进,从头部出,所以是正着的)

当然Back inserters只有在提供有push_back()成员函数的容器中才能派上用场,即只能用在vectoe,deque,list(序列式容器)

2.Front inserters

其内部调用了front_back(),这种动作会逆转被安插的元素次序(从头部插入,又从头部出,所以后进的先出了,所以反了)

Front inserters只有在提供有front_back()成员函数的容器中才能派上用场,即只能用在deque,list

3.General inserters

可用户所有容器,也是唯一可用于关联式容器的inserter。

比如对第三个coll4进行了如下更改:

coll1.push_back(3);
    cout << endl;
    multiset<int>coll4;
    copy(coll1.begin(), coll1.end(), inserter(coll4,coll4.begin()));
    for (auto pos = coll4.begin(); pos != coll4.end(); ++pos) {
        cout << *pos << " ";
    }

虽然coll的在尾部加了一个值为3的元素,但是输出仍然是排好序的1 2 3 3 4 5 6 7 8 9

2.流迭代器(Stream Iterators)

流迭代器是一种用来读写stream的迭代器,使得来自键盘的输入像是一个集群,能够从中读取内容,同理也可以把算法输出结果重新导向到某个文件或者屏幕上。

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <iterator>
using namespace std;

int main() {
    vector<string>coll;
    copy(istream_iterator<string>(cin), istream_iterator<string>(), back_inserter(coll));//从屏幕读取输入的文字

    sort(coll.begin(), coll.end());//排序
    unique_copy(coll.begin(),coll.end(),ostream_iterator<string>(cout,"\n"));//打印到屏幕

    system("pause");
}
结果:

或者要对比数字的话可以把上的的string改成int :
1 2 3 4 5 6 8 10 5 9 4 7 3
^Z    //这里注意,采用了空行+ctrl+z的方式才能结束输入程序
1 
2
3
4
5
6
7
8
9
10

1.istream_iterotor<string>(cin)

从标准输入流cin读取数据的istream iterator,每当算法企图处理下一个元素时,istream iterator就会将这种企图转化为cin>>string;

2.istream_iterotor<string>()

调用istream iterators的默认构造函数,产生一个代表“流结束符号”的迭代器,作为区间终点,在网上找的要用ctrl+c来结束

3. unique_copy(coll.begin(),coll.end(),ostream_iterator<string>(cout,"\n"));

将其中所有元素拷贝到目标端cout,处理过程中算法unique_copy会消除重复值

其中ostream_iterator<string>(cout,"\n")会产生一个output stream iterator通过operator<<向cout写入strings,cout之后的第二个参数(可以省略)被用来作为元素间的分隔符

3.逆向迭代器(reverse iterators)

以逆向方式进行所有操作。它将递增运算转换成递减运算,反之亦然,所有容器都可以通过成员函数rbegin()和rend()产生reverse iterators

#include <iostream>
#include <vector>
#include <list>
#include <deque>
#include <set>
#include <algorithm>
#include <iterator>
using namespace std;

int main() {
    vector<int>coll;
    for (int i = 1; i <= 9; ++i) {
        coll.push_back(i);
    }
    
    copy(coll.rbegin(), coll.rend(), ostream_iterator<int>(cout," "));
    cout << endl;
    system("pause");
}

结果为:

9 8 7 6 5 4 3 2 1

1.coll.rbegin()

指向集群的结尾位置(书上说是最后元素的下一个位置,我觉得不对)

cout << *coll.rbegin() << endl;
    cout << *coll.rend() << endl;

通过这个来测试,第一个输出的是9,也就是最后一个位置

2.coll.rend()

指向容器内第一个元素的前一个元素

因此上面那个测试第二个是无法输出的,程序终止了。并且注意,*coll.rend()与*coll.end()没有定义,当某个位置上并无合法元素时,永远不要用opterator*或者opterator->

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!