一.迭代器的基本概念
1.什么是迭代器
迭代器是一种对象,它提供了一种访问容器中各个元素的方法,同时隐藏了容器内部的实现细节。简单来说,迭代器就像是一个指针,它可以指向容器中的某个元素,并且能够通过一些操作(如++、–)来移动到容器中的其他元素。
简而言之,迭代器是一种检查容器内元素并且遍历容器内元素的数据类型。
2.迭代器的作用
迭代器本质上是一个抽象的“指针”,它提供对一个容器中的对象的访问方法,并且定义了容器中对象的范围。它提供了以下功能:
- 访问元素:通过解引用操作符(*)获取当前元素的值。
- 移动位置:通过递增(++)或递减(–)操作符在容器中移动。
- 比较位置:通过比较操作符(== 和 !=)判断两个迭代器是否指向同一个位置。
为什么要使用迭代器:
- STL提供了多种容器,每种容器的实现原理各不相同,如果没有迭代器我们需要记住每一种容器中对象的访问方法,很显然这样会变得非常麻烦。
- STL提供的许多容器中都实现了一个迭代器用于对容器中对象的访问,虽然每个容器中的迭代器的实现方式不一样,但是对于用户来说操作方法是一致的,也就说通过迭代器统一了对所有容器的访问方式。
例如:访问当前元素的下一个元素我们可以通过迭代器自增进行访问。
3.迭代器的本质
迭代器是容器类中专门实现的一个访问容器中数据的内嵌类(类中类)
为了统一每个容器中对于迭代器的操作,在容器类中会使用typedef将迭代器类进行别名定义,别名为:iterator
迭代器类对容器中元素的访问方式:指针
迭代器类的具体实现:为了隐藏每个容器中迭代器的具体实现,也为了统一用户对于每个容器中迭代器的访问方式,用户可以把迭代器当成一个指针对容器中的元素进行访问。但是因为迭代器不是指针,因此在迭代器类中我们需要对 * 、->、前置++/–、后置++/–等操作符进行重载。
T &operator*() const {}
node<T>*operator->() const {}
list_iterator &operator++() {}
list_iterator operator++(int) {}
bool operator==(const list_iterator &t) const {}
bool operator!=(const list_iterator &t) const {}
4.迭代器的分类
C++ 迭代器的分类
C++ STL 定义了五种类型的迭代器,每种迭代器提供了不同级别的功能,适用于不同类型的容器。这五种迭代器分别是:
- 输入迭代器(Input Iterator)
- 输出迭代器(Output Iterator)
- 前向迭代器(Forward Iterator)
- 双向迭代器(Bidirectional Iterator)
- 随机访问迭代器(Random Access Iterator)
二.以vector容器的迭代器为例
每种容器类型都定义了自己的迭代器类型,如vector:
vector<int>::iterator iter; //变量名为iter。
1.vector容器迭代器类中的成员函数
vector容器的迭代器属于“随机访问迭代器”:迭代器一次可以移动多个位置
最为常用的是begin和end函数。
一、begin() 和 end() 函数
- begin() 函数
begin() 函数返回一个迭代器,指向 std::vector 的第一个元素。
返回值:
返回一个普通迭代器(iterator),如果容器是 const 的,则返回 const_iterator。
用途:
用于遍历容器的起始位置。
可以用于 STL 算法的起始范围。 - end() 函数
end() 函数返回一个迭代器,指向 std::vector 的最后一个元素之后的位置(即“尾后迭代器”)。
返回值:
返回一个普通迭代器(iterator),如果容器是 const 的,则返回 const_iterator。
用途:
用于遍历容器的结束位置。
可以用于 STL 算法的结束范围。
注意:end() 返回的迭代器不指向任何有效元素,因此不能直接解引用。
二、cbegin() 和 cend() 函数
- cbegin() 函数
cbegin() 函数返回一个常量迭代器(const_iterator),指向 std::vector 的第一个元素。
返回值:
返回一个 const_iterator。
用途:
用于只读访问容器中的元素。
适用于 const 容器或需要保证元素不被修改的场景。 - cend() 函数
cend() 函数返回一个常量迭代器(const_iterator),指向 std::vector 的最后一个元素之后的位置。
返回值:
返回一个 const_iterator。
用途:
用于只读访问容器中的元素。
适用于 const 容器或需要保证元素不被修改的场景。
三、rbegin() 和 rend() 函数
- rbegin() 函数
rbegin() 函数返回一个反向迭代器(reverse_iterator),指向 std::vector 的最后一个元素。
返回值:
返回一个普通反向迭代器(reverse_iterator),如果容器是 const 的,则返回 const_reverse_iterator。
用途:
用于反向遍历容器。
可以用于 STL 算法的反向范围。 - rend() 函数
rend() 函数返回一个反向迭代器(reverse_iterator),指向 std::vector 的第一个元素之前的位置。
返回值:
返回一个普通反向迭代器(reverse_iterator),如果容器是 const 的,则返回 const_reverse_iterator。
用途:
用于反向遍历容器。
注意:rend() 返回的迭代器不指向任何有效元素,因此不能直接解引用。
四、crbegin() 和 crend() 函数
- crbegin() 函数
crbegin() 函数返回一个常量反向迭代器(const_reverse_iterator),指向 std::vector 的最后一个元素。
返回值:
返回一个 const_reverse_iterator。
用途:
用于只读访问容器中的元素。
适用于 const 容器或需要保证元素不被修改的场景。 - crend() 函数
crend() 函数返回一个常量反向迭代器(const_reverse_iterator),指向 std::vector 的第一个元素之前的位置。
返回值:
返回一个 const_reverse_iterator。
用途:
用于只读访问容器中的元素。
适用于 const 容器或需要保证元素不被修改的场景。
总结
- •begin() 和 end():
◦ 用于正向遍历容器。
◦ begin() 返回指向第一个元素的迭代器。
◦ end() 返回指向最后一个元素之后位置的迭代器。 - • cbegin() 和 cend():
◦ 用于只读访问容器中的元素。
◦ 返回常量迭代器(const_iterator)。 - • rbegin() 和 rend():
◦ 用于反向遍历容器。
◦ rbegin() 返回指向最后一个元素的反向迭代器。
◦ rend() 返回指向第一个元素之前位置的反向迭代器。 - • crbegin() 和 crend():
◦ 用于只读访问容器中的元素。
◦ 返回常量反向迭代器(const_reverse_iterator)。
2.迭代器的算术操作
随机访问迭代器是最强大的迭代器类型,它支持以下算术操作:
- 加法(+):通过 it + n 将迭代器向前移动 n 个位置。
- 减法(-):通过 it - n 将迭代器向后移动 n 个位置。
- 自增(++):通过 ++it 将迭代器向前移动一个位置。
- 自减(–):通过 --it 将迭代器向后移动一个位置。
- 比较操作(<、>、<=、>=):比较两个迭代器的位置。
- 距离计算(std::distance):计算两个迭代器之间的距离。
这些操作使得随机访问迭代器能够高效地在容器中移动和访问元素。
3.迭代器失效
在对容器进行插入、删除或重新分配内存等操作时,迭代器可能会失效。因此,在这些操作后,需要重新获取迭代器。
(1)插入元素导致迭代器失效
int main()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);//v有三个元素 1,2,3,4vector<int>::iterator it1 = v.begin() + 3;v.insert(it1, 8);//插入一个8cout << *it1 << endl;//输出it位置的元素return 0;
}
运行上面这段代码,我们会发现输出的结果并不是8,甚至有可能会导致程序崩溃。这是为什么呢?
因为在insert时,vector可能需要进行扩容,而扩容的本质是new一块新的空间,再将数据迁移过去。而我们知道,迭代器的内部是通过指针访问容器中的元素的,而插入后,若vector扩容,则原有的数据被释放,指向原有数据的迭代器就成了野指针,所以迭代器失效了。
而解决的办法很简单,insert函数提供了返回值,这个返回值是指向插入之后的val的迭代器。我们只需要保存返回的迭代器,并使用这个新的迭代器即可。
也就是
itl = v.insert(it1, 8);
(2)删除元素导致迭代器失效
vector<int> cont ={1,2,3,3,3,4,5,5,5,6};
for (iter = cont.begin(); iter != cont.end();iter++)
{if (*iter == 3)cont.erase(iter);
}
对于序列式容器(如vector,deque),序列式容器就是数组式容器,删除当前的iterator会使后面所有元素的iterator都失效。这是因为vetor,deque使用了连续分配的内存,删除一个元素导致后面所有的元素会向前移动一个位置。所以不能使用erase(iter++)的方式,还好erase方法可以返回下一个有效的iterator。
解决办法:
vector<int> cont ={1,2,3,3,3,4,5,5,5,6};for (iter = cont.begin(); iter != cont.end();) { if (*iter == 3) iter = cont.erase(iter); else iter++;}