c++之迭代器

一.迭代器的基本概念

1.什么是迭代器

迭代器是一种对象,它提供了一种访问容器中各个元素的方法,同时隐藏了容器内部的实现细节。简单来说,迭代器就像是一个指针,它可以指向容器中的某个元素,并且能够通过一些操作(如++、--)来移动到容器中的其他元素。

简而言之,迭代器是一种检查容器内元素并且遍历容器内元素的数据类型。

2.迭代器的作用

迭代器本质上是一个抽象的"指针",它提供对一个容器中的对象的访问方法,并且定义了容器中对象的范围。它提供了以下功能:

  1. 访问元素:通过解引用操作符(*)获取当前元素的值。
  2. 移动位置:通过递增(++)或递减(--)操作符在容器中移动。
  3. 比较位置:通过比较操作符(== 和 !=)判断两个迭代器是否指向同一个位置。

为什么要使用迭代器:

  1. STL提供了多种容器,每种容器的实现原理各不相同,如果没有迭代器我们需要记住每一种容器中对象的访问方法,很显然这样会变得非常麻烦。
  2. STL提供的许多容器中都实现了一个迭代器用于对容器中对象的访问,虽然每个容器中的迭代器的实现方式不一样,但是对于用户来说操作方法是一致的,也就说通过迭代器统一了对所有容器的访问方式。
    例如:访问当前元素的下一个元素我们可以通过迭代器自增进行访问。

3.迭代器的本质

迭代器是容器类中专门实现的一个访问容器中数据的内嵌类(类中类)

为了统一每个容器中对于迭代器的操作,在容器类中会使用typedef将迭代器类进行别名定义,别名为:iterator

迭代器类对容器中元素的访问方式:指针

迭代器类的具体实现:为了隐藏每个容器中迭代器的具体实现,也为了统一用户对于每个容器中迭代器的访问方式,用户可以把迭代器当成一个指针对容器中的元素进行访问。但是因为迭代器不是指针,因此在迭代器类中我们需要对 * 、->、前置++/--、后置++/--等操作符进行重载。

c 复制代码
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 定义了五种类型的迭代器,每种迭代器提供了不同级别的功能,适用于不同类型的容器。这五种迭代器分别是:

  1. 输入迭代器(Input Iterator)
  2. 输出迭代器(Output Iterator)
  3. 前向迭代器(Forward Iterator)
  4. 双向迭代器(Bidirectional Iterator)
  5. 随机访问迭代器(Random Access Iterator)

二.以vector容器的迭代器为例

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

c 复制代码
vector<int>::iterator iter; //变量名为iter。

1.vector容器迭代器类中的成员函数

vector容器的迭代器属于"随机访问迭代器 ":迭代器一次可以移动多个位置

最为常用的是begin和end函数。

一、begin() 和 end() 函数

  1. begin() 函数
    begin() 函数返回一个迭代器,指向 std::vector 的第一个元素。
    返回值:
    返回一个普通迭代器(iterator),如果容器是 const 的,则返回 const_iterator。
    用途:
    用于遍历容器的起始位置。
    可以用于 STL 算法的起始范围。
  2. end() 函数
    end() 函数返回一个迭代器,指向 std::vector 的最后一个元素之后的位置 (即"尾后迭代器")。
    返回值:
    返回一个普通迭代器(iterator),如果容器是 const 的,则返回 const_iterator。
    用途:
    用于遍历容器的结束位置。
    可以用于 STL 算法的结束范围。
    注意:end() 返回的迭代器不指向任何有效元素,因此不能直接解引用。

二、cbegin() 和 cend() 函数

  1. cbegin() 函数
    cbegin() 函数返回一个常量迭代器(const_iterator),指向 std::vector 的第一个元素。
    返回值:
    返回一个 const_iterator。
    用途:
    用于只读访问容器中的元素。
    适用于 const 容器或需要保证元素不被修改的场景。
  2. cend() 函数
    cend() 函数返回一个常量迭代器(const_iterator),指向 std::vector 的最后一个元素之后的位置。
    返回值:
    返回一个 const_iterator。
    用途:
    用于只读访问容器中的元素。
    适用于 const 容器或需要保证元素不被修改的场景。

三、rbegin() 和 rend() 函数

  1. rbegin() 函数
    rbegin() 函数返回一个反向迭代器(reverse_iterator),指向 std::vector 的最后一个元素。
    返回值:
    返回一个普通反向迭代器(reverse_iterator),如果容器是 const 的,则返回 const_reverse_iterator。
    用途:
    用于反向遍历容器。
    可以用于 STL 算法的反向范围。
  2. rend() 函数
    rend() 函数返回一个反向迭代器(reverse_iterator),指向 std::vector 的第一个元素之前的位置。
    返回值:
    返回一个普通反向迭代器(reverse_iterator),如果容器是 const 的,则返回 const_reverse_iterator。
    用途:
    用于反向遍历容器。
    注意:rend() 返回的迭代器不指向任何有效元素,因此不能直接解引用。

四、crbegin() 和 crend() 函数

  1. crbegin() 函数
    crbegin() 函数返回一个常量反向迭代器(const_reverse_iterator),指向 std::vector 的最后一个元素。
    返回值:
    返回一个 const_reverse_iterator。
    用途:
    用于只读访问容器中的元素。
    适用于 const 容器或需要保证元素不被修改的场景。
  2. crend() 函数
    crend() 函数返回一个常量反向迭代器(const_reverse_iterator),指向 std::vector 的第一个元素之前的位置。
    返回值:
    返回一个 const_reverse_iterator。
    用途:
    用于只读访问容器中的元素。
    适用于 const 容器或需要保证元素不被修改的场景。

总结

  1. •begin() 和 end():
    ◦ 用于正向遍历容器。
    ◦ begin() 返回指向第一个元素的迭代器。
    ◦ end() 返回指向最后一个元素之后位置的迭代器。
  2. • cbegin() 和 cend():
    ◦ 用于只读访问容器中的元素。
    ◦ 返回常量迭代器(const_iterator)。
  3. • rbegin() 和 rend():
    ◦ 用于反向遍历容器。
    ◦ rbegin() 返回指向最后一个元素的反向迭代器。
    ◦ rend() 返回指向第一个元素之前位置的反向迭代器。
  4. • crbegin() 和 crend():
    ◦ 用于只读访问容器中的元素。
    ◦ 返回常量反向迭代器(const_reverse_iterator)。

2.迭代器的算术操作

随机访问迭代器是最强大的迭代器类型,它支持以下算术操作:

  1. 加法(+):通过 it + n 将迭代器向前移动 n 个位置。
  2. 减法(-):通过 it - n 将迭代器向后移动 n 个位置。
  3. 自增(++):通过 ++it 将迭代器向前移动一个位置。
  4. 自减(--):通过 --it 将迭代器向后移动一个位置。
  5. 比较操作(<、>、<=、>=):比较两个迭代器的位置。
  6. 距离计算(std::distance):计算两个迭代器之间的距离。

这些操作使得随机访问迭代器能够高效地在容器中移动和访问元素。

3.迭代器失效

在对容器进行插入、删除或重新分配内存等操作时,迭代器可能会失效。因此,在这些操作后,需要重新获取迭代器。

(1)插入元素导致迭代器失效

c 复制代码
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,4

    vector<int>::iterator it1 = v.begin() + 3;
    v.insert(it1, 8);//插入一个8
    cout << *it1 << endl;//输出it位置的元素
    return 0;
}

运行上面这段代码,我们会发现输出的结果并不是8,甚至有可能会导致程序崩溃。这是为什么呢?

因为在insert时,vector可能需要进行扩容,而扩容的本质是new一块新的空间,再将数据迁移过去。而我们知道,迭代器的内部是通过指针访问容器中的元素的,而插入后,若vector扩容,则原有的数据被释放,指向原有数据的迭代器就成了野指针,所以迭代器失效了。

而解决的办法很简单,insert函数提供了返回值,这个返回值是指向插入之后的val的迭代器。我们只需要保存返回的迭代器,并使用这个新的迭代器即可。

也就是

c 复制代码
itl = v.insert(it1, 8);

(2)删除元素导致迭代器失效

c 复制代码
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。

解决办法:

c 复制代码
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++;
 }
相关推荐
君义_noip1 分钟前
信息学奥赛一本通 1514:【例 2】最大半连通子图 | 洛谷 P2272 [ZJOI2007] 最大半连通子图
c++·图论·信息学奥赛
努力学习的小廉14 分钟前
【C++】 —— 笔试刷题day_7
开发语言·c++
wuqingshun31415920 分钟前
蓝桥杯 整数变换
数据结构·c++·算法·职场和发展·蓝桥杯
禁默1 小时前
C++11之深度理解lambda表达式
开发语言·c++
wen__xvn1 小时前
每日一题洛谷P8717 [蓝桥杯 2020 省 AB2] 成绩分析c++
c++·算法·蓝桥杯
Tadecanlan1 小时前
[C++面试] 你了解transform吗?
开发语言·c++
f狐0狸x1 小时前
【蓝桥杯每日一题】3.25
开发语言·数据结构·c++·算法·蓝桥杯
敲上瘾2 小时前
定长内存池原理及实现
c++·缓存·aigc·池化技术
扫地的小何尚2 小时前
NVIDIA TensorRT 深度学习推理加速引擎详解
c++·人工智能·深度学习·gpu·nvidia·cuda
UestcXiye4 小时前
《TCP/IP网络编程》学习笔记 | Chapter 21:异步通知 I/O 模型
c++·计算机网络·ip·tcp