c++之迭代器

发布于:2025-03-24 ⋅ 阅读:(24) ⋅ 点赞:(0)

一.迭代器的基本概念

1.什么是迭代器

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

2.迭代器的作用

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

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

为什么要使用迭代器:

  1. STL提供了多种容器,每种容器的实现原理各不相同,如果没有迭代器我们需要记住每一种容器中对象的访问方法,很显然这样会变得非常麻烦。
  2. 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 定义了五种类型的迭代器,每种迭代器提供了不同级别的功能,适用于不同类型的容器。这五种迭代器分别是:

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

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

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

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)插入元素导致迭代器失效

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的迭代器。我们只需要保存返回的迭代器,并使用这个新的迭代器即可。
也就是

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++;
 }

网站公告

今日签到

点亮在社区的每一天
去签到