一、定义
迭代器是一种检查容器内元素并且遍历容器内元素的数据类型。
**【引用自:C++迭代器(iterator)_c++ iterator_NiUoW的博客-CSDN博客】**迭代器是一个变量,相当于容器和操纵容器的算法之间的中介。C++更趋向于使用迭代器而不是数组下标操作,因为标准库为每一种标准容器(如vector、map和list等)定义了一种迭代器类型,而只有少数容器(如vector)支持数组下标操作访问容器元素。可以通过迭代器指向你想访问容器的元素地址,通过*x打印出元素值。这和我们所熟知的指针极其类似。
C语言有指针,指针用起来十分灵活高效。C++语言有迭代器,迭代器相对于指针而言功能更为丰富。
vector,是数组实现的,也就是说,只要知道数组的首地址,就能访问到后面的元素。所以,我们可以通过访问vector的迭代器来遍历vector容器元素。
List,是链表实现的,我们知道,链表的元素都存储在一段不是连续的地址空间中。我们需要通过next指针来访问下一个元素。那么,我们也可以通过访问list的迭代器来实现遍历list容器元素。
由此可见,迭代器和容器是密不可分的、紧密相连的的关系。不同的容器,它们的迭代器也是不同的,但是它们的迭代器功能是一样的。假如没有迭代器,由于vector和list容器的存储特点,你需要两种算法去实现遍历vector和list容器的功能,复杂且低效。有了迭代器,遍历容器的效率会大大提高。
二、为什么要使用迭代器
使用STL(Standard Template Library)中的迭代器有以下几个好处:
-
统一的访问方式:STL的迭代器提供了一种统一的访问容器元素的方式,无论是数组、链表、集合还是映射,都可以通过相同的迭代器接口进行遍历和访问。这种统一性简化了代码的书写,并提高了代码的可读性和可维护性。
-
安全的访问操作:迭代器在设计上考虑了容器的边界情况,确保不会越界访问或访问非法内存引起崩溃。迭代器提供了递增、递减等操作符,使得在容器中前进或后退一个位置变得简单和安全。
-
灵活的遍历方式:迭代器支持正向遍历、反向遍历以及跳跃式遍历等方式,使得在不同的情况下选择合适的遍历方式变得方便。比如,可以使用反向迭代器从容器的末尾向前遍历,或者使用跳跃式迭代器按照一定规则跳过一些元素。
-
可算法化处理:STL提供了丰富的算法,如排序、查找、拷贝、删除等,这些算法可以直接操作迭代器,而不需要关心具体容器的实现。使用迭代器作为算法的参数,使得代码可复用性更强,可以将同一套算法应用于不同类型的容器。
-
可组合的操作:迭代器的操作是可以组合的,可以在不同的操作之间进行链式调用,形成更复杂的操作序列。这种可组合性使得代码更加灵活,可以根据需求自由组合和定制迭代器操作,实现更多样化的功能。
总之,STL中的迭代器提供了一种抽象的、统一的访问容器元素的方式,使得我们可以以一种通用的方式处理各种不同类型的容器。这样做可以提高代码的重用性、可读性和可维护性,同时还能够充分利用STL提供的丰富算法,简化开发过程并提高效率。
另一种解释:【取自C++STL之迭代器(iterator)详解_c++迭代器-CSDN博客】
(1) STL提供每种容器的实现原理各不相同,如果没有迭代器我们需要记住每一种容器中对象的访问方法,很显然这样会变得非常麻烦。
(2) 每个容器中都实现了一个迭代器用于对容器中对象的访问,虽然每个容器中的迭代器的实现方式不一样,但是对于用户来说操作方法是一致的,也就说通过迭代器统一了对所有容器的访问方式。
(3) 迭代器的使用可以提高编程的效率。
三、迭代器的使用
3.1 基本使用方法
(1) 首先要定义一个迭代器类型变量(这里以vector容器为例)。
定义方法如下:容器类名::iterator 迭代器名;
如要定义vector容器的迭代器:vector<int>::iterator iter;(这里的iter是变量名,可以自定义)
(2) 接下来要利用迭代器访问容器数据
先要了解几个成员函数,如下表所示。
|----------|-----------------------------------------------------------------------------|
| 成员函数 | 功能 |
| begin() | 返回指向容器中第一个元素的正向迭代器;如果是 const 类型容器,在该函数返回的是常量正向选代器。 |
| end() | 返回指向容器最后一个元素之后一个位置的正向迭代器,如果是 onst 类型容器,在该函数返回的是常量正向迭代器。此函数通常和 begin() 搭配使用。 |
| rbegin() | 返回指向最后一个元素的反向迭代器,如果是 const 类型容器,在该函数返回的是常量反向迭代器。 |
| rend() | 返回指向第一个元素之前一个位置的反向迭代器。如果是 const 类型容器,在该函数返回的是常量反向迭代器。此函数通常和 begin() 搭配使甲。 |
这里的 end()、rend() 函数要注意,不是指向容器最后一个元素 ,而是后一个位置(看图)
(3) 示例
cpp
#include<iostream>
#include<vector>
int main() {
std::vector <int> vec; // 定义向量对象vec
vec.push_back(2);
vec.push_back(3);
vec.push_back(5);
vec.push_back(9);
// 至此,向量vec中包含四个元素,分别是2,3,5,9
std::vector<int>::iterator iter; // 定义迭代器类型变量iter
iter = vec.begin(); // 变量被赋值为指向第一个元素的迭代器
std::cout << *iter << std::endl;
iter++; // 可以用自增操作,让iter指向下一个元素
std::cout << *iter << std::endl;
iter = vec.begin() + 2; // 让iter指向容器中的第三个位置
std::cout << *iter << std::endl;
}
输出结果:
2 3 5
注意:vector容器迭代器属于随机访问迭代器 ,可以一次移动多个位置,如iter=v1.begin()+2;
也可以用iter=iter+2;
3.2 容器数据的遍历
3.2.1 常见方法(正序遍历)
cpp
for (auto it = container.begin(); it != container.end(); ++it) {
// process element *it
}
3.2.2 常见方法(逆序遍历)
cpp
for (auto it = container.rbegin(); it != container.rend(); ++it) {
// process element *it
}
其中,auto
是C++11的关键字,它可以自动推导迭代器类型。循环中,首先使用begin()
函数获取容器起始位置的迭代器,然后每次使用递增运算符前进一个位置,直到迭代器等于end()
函数返回的迭代器时结束循环。
3.2.3 注意问题
需要注意的是,在处理空容器或只有一个元素的容器时,begin()
和end()
函数返回的迭代器是相同的,因此在循环中不能使用!=
运算符进行比较,而应该使用 <
或 >
运算符判断是否越界。
当一个容器为空或只有一个元素时,`begin()`和`end()`函数返回的迭代器是同一个位置,因此使用`!=`运算符进行比较可能会得到错误的结果。
对于空容器,`begin()`和`end()`函数都返回指向末尾的迭代器,因为没有元素可以遍历。这种情况下,应该直接判断迭代器是否等于末尾迭代器,如下所示:
cpp
std::vector<int> v;
if (v.begin() == v.end()) {
// 处理空容器的情况
}
对于只有一个元素的容器,`begin()`和`end()`函数虽然会返回不同的迭代器,但是它们指向的是同一个位置,即容器中唯一的元素。这种情况下,应该使用`<`或`>`运算符进行比较,如下所示:
cpp
std::vector<int> v{1};
for (auto it = v.begin(); it < v.end(); ++it) { // 注意这里使用<
std::cout << *it << std::endl;
}
同样地,在循环中也可以使用`>=`或`<=`运算符判断是否越界,但是更好的做法是使用标准库提供的迭代器判等函数`std::equal()`、`std::lexicographical_compare()`等,这些函数会自动处理空容器和只有一个元素的容器的情况,并提供更好的可读性和代码健壮性。
3.2.4 遍历应用示例
cpp
#include<iostream>
#include<vector>
int main() {
std::vector <int> vec; // 定义向量对象vec
vec.push_back(2);
vec.push_back(3);
vec.push_back(5);
vec.push_back(9);
// 至此,向量vec中包含四个元素,分别是2,3,5,9
// 遍历向量vec中的所有元素
for (auto iter = vec.begin(); iter != vec.end(); iter++) {
std::cout << *iter << " ";
}
std:: cout << std::endl;
}
注意:上面说到end()函数是指向容器的最后一个元素的后一个位置,所以当迭代器指向最后的后一个位置时结束遍历,便可以访问所有容器元素。
输出结果:
2 3 5 9
四、不同容器的迭代器(iterator)的功能
vector 随机访问
deque 随机访问
list 双向
set / multiset 双向
map / multimap 双向
stack 不支持迭代器
queue 不支持迭代器
priority_queue 不支持迭代器
参考文献
C++STL之迭代器(iterator)详解_c++迭代器-CSDN博客
C++迭代器(iterator)_c++ iterator_NiUoW的博客-CSDN博客
关于迭代器失效的几种情况-CSDN博客(值得阅读)