当前位置: 首页 > news >正文

C++ 迭代器

1.用途:

像我们之前学习的容器map,vector等,如果需要遍历该怎么做呢?这些容器大部分对下标式遍历,无法像数组灵活使用,也包括增删改查,因为它们的特性,所以需要一种其他的方法。

那么迭代器就是一种很好的替代方法。

(注意有些也可使用下标的方法,因为重载了[]操作符,这里不做过多说明,建议用迭代器进行操作)

迭代器有一组通用的方法,以便它们能在不同的容器都能使用。

迭代器的本质是一个类,封装了元素的指针(也可以是引用等),可以看作是对指针的一种封装,然后在这个类里重载了* ->  ++等指针相关的操作符,使其模仿指针的行为,表面上像指针一样使用。

如果有时候你不能理解某种情况,你可以试着把迭代器当成指针(仅为便于理解)

下面我们来看一下具体用法。

1.1访问元素

begin方法,返回容器中第一个元素的迭代器。

end方法,容器中最后一个元素的后一个迭代器(非最后一个元素的迭代器)

示例:

#include<iostream>
#include<vector>
using namespace std;
int main()
{vector<int> vec = { 1,2,3,4,5 };vector<int>::iterator it = vec.begin();for (; it != vec.end(); it++){cout << *it << " " ;}
}

结果:

迭代器的定义,每种类型都是不一样的,比如map的迭代器,需要指定类型,这个就跟指针一样的道理,char *p,int *p;这样,是不同的。

it++ 跟指针一样,移到下一个元素。 迭代器指向下一个元素。

然后通过*it访问元素,就像指针取值一样。

1.2更改元素
#include<iostream>
#include<vector>
using namespace std;
int main()
{vector<int> vec = { 1,2,3,4,5 };vector<int>::iterator it = vec.begin();for (; it != vec.end(); it++){if (*it == 3)  //像指针的方式改值即可*it = 6;}cout << vec[2] << endl;
}

结果:

输出为6,说明正常改值。 

1.3 auto

如果是map类型,使用迭代器,需要这样定义:(根据生成的类型指定)

#include<iostream>
#include<string>
#include<map>
using namespace std;
int main()
{map<int, string> mp = { {1,"one"},{2,"two"}, {3,"three"} };map<int, string>::iterator it = mp.begin();for (; it != mp.end(); it++){cout << it->first<< ":" << it->second << endl; //也可(*it).first访问,即分别是类指针 类对象访问成员的方式}}

map中每一个元素都是pair对象,pair是一个类或者结构体,里面有成员first存储键,second存储值。所以迭代器指向的是pair。

可以看到,定义迭代器时每次需要指定类型有些不便。

我们可以使用auto,类型推导关键字,即编译器会根据初始化变量,自动推导类型。

比如 auto c='b',c就是个char 变量。

这个auto当然也支持迭代器类型。如下更改即可:

	//map<int, string>::iterator it = mp.begin();auto it = mp.begin();
2.获取指针

前面说过了,迭代器是对底层是对指针的封装,我们可以获取到这个指针,以便在某些特定情况下使用。有两种常用方法:

第一种,通过*解引用,然后再&取地址。

第二种,容器的data方法,返回底层元素数组的首地址。

如下示例:

#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{vector<int> vec = { 1, 2, 3, 4, 5 };//map<int, string>::iterator it = mp.begin();vector<int>::iterator it = vec.begin()+2;//获取第三个元素迭代器	int* p = &(*it); //解引用,再取地址,获取迭代器封装的指针cout << "解引用&获取方法:\n值:" << *p << "  地址:" << p << endl;int* p1 = vec.data()+2; //首地址+2,也是指向第三个元素的指针cout << "data获取方法:\n值:" << *p1 << "  地址:" << p1 << endl;}

结果:

可以看到,它们值和地址输出都一样,说明两种方法工作正常。

3.内存分配

像这些容器如vector,map等,它们的功能比数组强大的多(动态性方面),说明它们内部不可能像数组一样。比如int a[100];这样静态分配内存在栈上。那将必然失去它们的灵活性。

所以vector map等都是动态分配内存,如使用new等分配在堆上,那么它们内部则需要一套管理内存的方法,手动分配和释放。

我们通过方法也可以参与这个过程,比如这样定义:

std::vector<int> vec(3);

容器初始化分配3个int内存空间。

通过capacity()方法,我们可以查看容器的分配的内存空间大小.

示例:

#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{vector<int> vec(3);cout << vec.capacity() << endl;
}

 结果:

说明这个容器,目前可以容纳三个元素。

如果此时我们调用push_back再新增一个元素,这会触发内存重新匹配,因为已经超出了容器的上限,这个会造成什么问题呢?之前的迭代器会失灵,因为封装的指针地址已经失效了,这个是需要注意的。

我们可以通过输出地址来观察这个现象:

#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{vector<int> vec = { 1,2,3};auto it = vec.begin();int* p = &(*it);cout << "指针地址:" << p << "   迭代器取值:" << *it << endl;vec.push_back(4);//容器如果不够,此操作会触发内存重新分配以满足新的大小int* p1 = vec.data();//再取地址cout << "指针地址:" << p1 << endl;//此时再*it会报错,因为访问非法的内存。 需要重新获取迭代器}

结果:

两次地址不一样,说明已经重新分配。

如果要避免这种情况呢,该怎么做?提前分配好大小,如下:

#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{vector<int> vec = { 1,2,3 }; //注意这种行为 看情况有些分配的空间不一定是三,而是大于三,本例仅在为三的情况下。vec.reserve(4); //通过reserve分配内存空间为4cout << "已使用内存:" << vec.size() << "   容器总内存:" << vec.capacity() << endl;auto it = vec.begin();int* p = &(*it);cout << "指针地址:" << p << "   迭代器取值:" << *it << endl;vec.push_back(4);//不会触发重新分配int* p1 = vec.data();//再取地址cout << "指针地址:" << p1 <<"   迭代器取值:" << *it << endl; //地址一样,之前迭代器也可用}

结果:

可以看到,当已使用内存,并没有超过容器总内存时,再添加元素,并不会触发内存重新分配。

地址一样,之前的迭代器也可继续使用。 

相关文章:

  • 【深度学习】目标检测算法大全
  • 从模型加密到授权交付,CodeMeter赋能3D打印商业化全流程
  • 【Nginx配置域名以及ssl证书】
  • 学习黑客5 分钟深入浅出理解Windows User Accounts, Profiles, and Permissions
  • Spring Boot 的 CommandLineRunner
  • Docker原理与使用教程
  • WebSocket集成方案对比
  • 测试文章标题01
  • 用Trae+Claude写一个学习网络基础的小网站
  • 【Python 变量类型】
  • 日常组件复用与基于构件开发的本质区别
  • MySQL 学习(七)undo log、redo log、bin log 的作用以及持久化机制
  • 多令牌预测Multi-Token Prediction(MTP)
  • 高防云的主要优势表现在哪些方面?
  • RabbitMQ 工作模式
  • Android音频解码中的时钟同步问题:原理、挑战与解决方案
  • Power BI 实操案例,将度量值转化为切片器(动态切换分析指标)
  • Redis——达人探店
  • 产品思维30讲-(梁宁)--实战2
  • 【Linux】在Arm服务器源码编译onnxruntime-gpu的whl
  • 字母哥动了离开的心思,他和雄鹿队的缘分早就到了头
  • 欧阳娜娜担任江西吉安文化旅游大使
  • 四姑娘山一游客疑因高反身亡,镇卫生院:送到时已很严重
  • 宝妈称宝宝在粽子中吃出带血创可贴,来伊份:已内部排查
  • 黄土是他们的气质:打破宁夏当代油画创作的沉寂
  • 人民日报读者点题·共同关注:今天我们为什么还需要图书馆?