C++ STL array随机访问迭代器(精讲版)
在笔记一中,已经对迭代器做了一定的介绍,STL为array容器配备了随机访问迭代器,该类迭代器是功能最强大的迭代器。本笔记将详细介绍array容器的迭代器用法。
在array容器的模板类中,和随机访问迭代器相关的成员函数如表1所示。
表1 array支持迭代器的成员函数
成员函数 | 功能 |
---|---|
begin() | 返回指向容器中第一个元素的正向迭代器;如果是const类型容器,在该函数返回的是常量正向迭代器。 |
end() | 返回指向容器最后一个元素之后一个位置的正向迭代器;如果是const类型容器,在该函数返回的是常量正向迭代器。此函数通常和begin()搭配使用。 |
rbegin() | 返回指向最后一个元素的反向迭代器;如果是const类型容器,在该函数返回的是常量反向迭代器。 |
rend() | 返回指向第一个元素之前一个位置的反向迭代器。如果是const类型容器,在该函数返回的是常量反向迭代器。此函数通常和rbegin()搭配使用。 |
cbegin() | 和 begin() 功能类似,只不过其返回的迭代器类型为常量正向迭代器,不能用于修改元素。 |
cend() | 和 end() 功能相同,只不过其返回的迭代器类型为常量正向迭代器,不能用于修改元素。 |
crbegin() | 和 rbegin() 功能相同,只不过其返回的迭代器类型为常量反向迭代器,不能用于修改元素。 |
crend() | 和 rend() 功能相同,只不过其返回的迭代器类型为常量反向迭代器,不能用于修改元素。 |
除此之外,C++11标准新增的begin()和end()函数,当操作对象为array容器时,也和迭代器有关,其功能分别和表1中的begin(),end()成员函数相同。
这些成员函数的具体功能如图2所示。
可以看到,根据它们的功能并结合实际场景的需要,这些成员函数通常是成对使用的,即begin()/end(),rbegin()/rend(),cbegin()/cend(),crbegin()/crend()各自成对搭配使用。不仅如此,这4对中begin()/end()和cbegin()/cend()、rbegin()/rend()和crbegin()/crend()的功能大致是相同的(如图2所示),唯一的区别就在于其返回的迭代器能否用来修改元素值。
值得一提的是,以上函数在实际使用的时,其返回值类型都可以使用auto关键字代替,编译器可以自行判断出该迭代器的类型。
begin()/end()和cbegin()/cend()
array容器模板类中的begin()和end()成员函数返回的都是正向的迭代器,它们分别指向 [ 首元素 ] 和 [ 尾元素+1 ] 的位置。在实际使用时,我们可以利用它们实现初始化容器或者遍历容器中元素的操作。
例如,可以在循环中显式地使用迭代器来初始化values容器的值:
#include <iostream>
//需要引入array头文件
#include <array>
using namespace std;
int main()
{
array<int,5>values;
int h = 1;
auto first = values.begin();
auto last = values.end();
//初始化values容器为{1,2,3,4,5}
while(first != last)
{
*first = h;
++first;
h++;
}
first = values.begin();
while(first != last)
{
cout << *first << " ";
++first;
}
return 0;
}
输出结果为:
1 2 3 4 5
可以看出,迭代器对象是由array对象的成员函数begin()和end()返回的。我们可以像使用普通指针那样上使用迭代器对象,比如代码中,在保存了元素值之后,使用前缀++运算符对first进行自增,当first等于end时,所有元素设完值,循环结束。
与此同时,还可以使用全局的begin()和end()函数来从容器中获取迭代器,因为当操作对象为array容器时,它们和begin()/end()成员函数是通用的。所以上面代码中,first和last还可以像下面这样定义:
auto first = std::begin(values);
auto last = std::end(values);
这样,容器中的一段元素可以由迭代器指定,这让我们有了对它们使用算法的可能。
需要注意的是,STL标准库,不是只有array容器,当迭代器指向容器中的一个特定元素时,它们不会保留任何关于容器本身的信息,所以我们无法从迭代器中判断,它是指向array容器还是指向vector容器。
除此之外,array模板类还提供了cbegin()和cend()成员函数,它们和begin()/end()唯一不同的是,前者返回的是const类型的正向迭代器,这就意味着,有cbegin()和cend()成员函数返回的迭代器,可以用来遍历容器内的元素,也可以访问元素,但是不能对所存储的元素进行修改。
举个例子:
#include <iostream>
//需引入array头文件
#include <array>
using namespace std;
int main()
{
array<int,5>values{1,2,3,4,5};
int h = 1;
auto first = values.cbegin();
auto last = values.cend();
//由于 *first为const类型,不能用来修改元素
//*first = 10;
//遍历容器并输出容器中所有元素
while(first != last)
{
//可以使用const类型迭代器访问元素
cout << *first << " ";
++first;
}
return 0;
}
此程序的*first = 10;语句,我们尝试使用first迭代器修改values容器中的值,如果取消注释并运行此程序,编译器会提示你“不能给常量赋值”,即 *first 是const类型常量,所以这么做是不对的。但16-21行代码遍历并访问容器的行为,是允许的。
rbegin()/rend() 和 crbegin()/crend()
array模板类中还提供了,rbegin()/rend()和crbegin()/crend()成员函数,它们每对都可以分别得到指向最后一个元素和第一个元素前一个位置的随机访问迭代器,又称它们为反向迭代器(如图2所示)。
需要注意的是,在使用反向迭代器进行++或–运算时,++指的是迭代器向左移一位,-- 指的是迭代器向右移动一位,即这两个运算符的功能也“互换”了。
反向迭代器用于以逆序的方式处理元素。例如:
#include <iostream>
//需要引入 array 头文件
#include <array>
using namespace std;
int main()
{
array<int, 5>values;
int h = 1;
auto first = values.rbegin();
auto last = values.rend();
//初始化 values 容器为 {5,4,3,2,1}
while (first != last)
{
*first = h;
++first;
h++;
}
//重新遍历容器,并输入各个元素
first = values.rbegin();
while (first != last)
{
cout << *first << " ";
++first;
}
return 0;
}
运行结果为:
1 2 3 4 5
可以看到,从最后一个元素开始循环,不仅完成了容器的初始化,还遍历输出了容器中的所有元素。结束迭代器指向第一个元素之前的位置,所以当 first 指向第一个元素并 +1 后,循环就结朿了。
在反向迭代器上使用 ++ 递增运算符,会让迭代器用一种和普通正向迭代器移动方向相反的方式移动。
当然,在上面程序中,我们也可以使用for循环:
for (auto first = values.rbegin(); first != values.rend(); ++first) {
cout << *first << " ";
}
crbegin()/crend() 组合和 rbegin()/crend() 组合的功能唯一的区别在于,前者返回的迭代器为 const 类型,即不能用来修改容器中的元素,除此之外在使用上和后者完全相同。