19.5 C++STL标准模板库大局观-迭代器的概念和分类

19.1 C++STL标准模板库大局观-STL总述、发展史、组成与数据结构谈
19.2 C++STL标准模板库大局观-容器分类与array、vector容器精解
19.3 C++STL标准模板库大局观-容器的说明和简单应用例续
19.4 C++STL标准模板库大局观-分配器简介、使用与工作原理说
19.5 C++STL标准模板库大局观-迭代器的概念和分类
19.6 C++STL标准模板库大局观-算法简介、内部处理与使用范例
19.7 C++STL标准模板库大局观-函数对象回顾、系统函数对象与范例
19.8 C++STL标准模板库大局观-适配器概念、分类、范例与总结

4.分配器简介、使用与工作原理说

在这里插入图片描述

  4.1 迭代器基本概念

    迭代器到底是什么?官方有一个比较确切的描述:迭代器是一个“可遍历STL容器全部或部分元素”的对象(为了方便理解,可以把迭代器理解为:行为类似于指针的对象)。迭代器用来表现容器中的某一个位置,迭代器是由容器来提供的。也就是说,一般来讲,是容器里面定义着迭代器的具体类型细节。
    既然迭代器可以理解成行为类似于指针的对象,那么对于指针,可以用如*p来读取指针所指向的内容,所以对于迭代器,用如*iter一般也能读取到迭代器所指向的内容。
    可以写一段简单的迭代器使用范例:

{
	vector<int> iv = { 100,200,300 }; //定义一个容器
	for (vector<int>::iterator iter = iv.begin(); iter != iv.end(); ++iter) //经典传统用法,这里用++,!=等运算符来对迭代器进行操作
	{
		cout << *iter << endl;
	}
}

  4.2 迭代器的分类

    迭代器是分种类的,这件事可能有些读者并不知道。这里讲一讲迭代器的分类,有了分类之后,再看一看怎样对应到这个分类。分类的依据是什么呢?依据的是迭代器的移动特性以及在这个迭代器上能做的操作。
    后面学习算法(函数)的时候,这些算法一般要求用迭代器作为形参,并且对迭代器的移动特性还有一定要求,到讲算法时会详细研究。
    迭代器与一个指针一样,到处跳,表示一个位置。分类也是根据它跳跃的能力来分的,每个分类都对应着一个struct结构。迭代器主要分为以下5类。

{
	//(1)输出型迭代器(Outputiterator)。
	struct output_iterator_tag  a;
	// (2)输入型迭代器(Input iterator)。
	struct input_iterator_tag b;
	// (3)前向迭代器(Forward iterator)。
	struct forward_iterator_tag c;
	// (4)双向迭代器(Bidirectional iterator)。
	struct bidirectional_iterator_tag d;
	// (5)随机访问迭代器(Random-access iterator)。
	struct random_access_iterator_tag e;
}

在这里插入图片描述
    当然也不是所有容器都有迭代器,有的容器没有,如stack(栈)、queue(队列)等容器就不提供迭代器。因为如stack,就是后进先出,而queue,就是先进先出,不需要遍历,也不可以用迭代器遍历,所以对于这种容器,STL中连迭代器都不提供。
在这里插入图片描述
    表中所谓随机读取,指的是跳过一定个数的元素,如当前位置在第1个元素这里,可以立即跳过3个元素直达第4个元素。
    那么能否通过写一些代码来验证某个容器中迭代器的种类是否和表19.1一致呢?笔者通过网络搜集了一些代码供参考:

//如下这些是函数重载,函数名字都叫_display_category
void _display_category(random_access_iterator_tag mytag)
{
	cout << "random_access_iterator_tag" << endl;
}
void _display_category(bidirectional_iterator_tag mytag)
{
	cout << "bidirectional_iterator_tag" << endl;
}
void _display_category(forward_iterator_tag mytag)
{
	cout << "forward_iterator_tag" << endl;
}
void _display_category(output_iterator_tag mytag)
{
	cout << "output_iterator_tag" << endl;
}
void _display_category(input_iterator_tag mytag)
{
	cout << "input_iterator_tag" << endl;
}
template <typename T>
void display_category(T iter)  //编译器能推导出类型来
{  
	cout << "---------------begin-------------------" << endl;	
	typename iterator_traits<T>::iterator_category cagy;//这个叫过滤器(萃取机),用来获取这个T迭代器类型的种类;这个写法是有点意思的,萃取机的功能比较厉害
	_display_category(cagy); //这里编译器能够帮助我们找到最适当的重载函数
	cout << "typeid(ite).name() = " << typeid(iter).name() << endl; //打印类型名称
	cout << "---------------end-------------------" << endl;
};
int main()
{
	//注意#include各种容器所包含的头文件
	//可能不同版本编译器如下名称多少会有区别
	display_category(array<int, 100>::iterator()); //加()用于产生临时对象		
	display_category(vector<int>::iterator());
	display_category(list<int>::iterator());
	display_category(map<int, int>::iterator());
	display_category(set<int>::iterator());
	//还可以增加许多自己想显示的容器内容
	//......
}

在这里插入图片描述
    通过结果可以看到,不同的容器,它们的迭代器种类是不一样的。读者也可以对上述代码进行扩展,以查看更多容器中的迭代器种类。
    回过头,笔者再把这几个迭代器的功能做个整理,引用官方的一些说法描述:

(1)输出型迭代器

struct_output_iterator_tag

    功能:能一步一步往前走,并且能够通过这个迭代器往容器中写数据。这种迭代器支持的常用操作如表所示
在这里插入图片描述

(2)输入型迭代器

struct_input_iterator_tag

    功能:一次一个以向前的方向来读取元素,按照这个顺序一个一个返回元素值。这种迭代器支持的常用操作如表所示。
在这里插入图片描述

(3)前向迭代器

struct_forword_iterator_tag

    功能:因为继承自输入型迭代器,因此它也能以向前的方向来读取元素,同时它也支持写入操作。这种迭代器支持的常用操作如表所示(最后1行是新增的操作)。
在这里插入图片描述

(4)前向迭代器

struct_bidirectional_iterator_tag

    功能:继承自前向迭代器,在前向迭代器基础之上增加了向回(反向)迭代,也就是迭代的位置可以往回退。这种迭代器支持的常用操作如表
在这里插入图片描述

(5)随机访问迭代器

struct_random_access_iterator_tag

    功能:继承自双向迭代器,在双向迭代器基础之上又增加了所谓的随机访问能力,也就是增减某个偏移量,能够计算距离,还支持一些关系运算等。这种迭代器支持的常用操作如表19.6所示(最后8行是新增的操作)。
在这里插入图片描述
    表19.6中有个以往没有的就是迭代器之间的减法操作,能计算迭代器之间的距离。
    对于常规使用迭代器,一般用不到这么多功能,最常用的功能是遍历一下容器中的数据。这里提到的迭代器功能既然有这么多,可以作为一种了解,将这些知识储备起来,方便日后使用时随时查阅。
    下面代码演示一个随机访问迭代器(演示了*iter、!=、++iter操作)。在main主函数中,加入如下代码(读者也可以根据需要自行增加各种演示代码):

{
	vector<int> iv = { 100,200,300 }; //定义一个容器
	for (vector<int>::iterator iter = iv.begin(); iter != iv.end(); ++iter) //经典传统用法,这里用++,!=等运算符来对迭代器进行操作。
	{
		cout << *iter << endl;
		*iter = 6; //容器中每个元素值修改为6
	}

    总结:随机访问迭代器看起来最灵活,因为随机访问迭代器支持的操作最多,如一次可以跳跃多个元素等。其所支持的容器array、vector的元素内存都是连续的,所以要跳到如第5个元素上,非常方便,做个加法运算,就立即能跳过去。deque这个双端队列容器虽然支持的迭代器也是随机访问迭代器,但这个容器是分段连续的,也就是说内存中它并不是真连续,虽然不是真连续,但它的迭代器仍旧是随机访问,也就是一次可以跳跃多个元素,这个设计还是挺精妙的。总之,支持随机访问迭代器的容器多数都是内存连续的。
    同时,通过本节的讲解也可以看到vector和list容器的区别:这两个容器所支持的迭代器类型不同,vector容器支持的是随机访问迭代器,list容器支持的是双向迭代器,没有随机访问迭代器支持这么多的迭代器操作。前面也讲过,vector容器和list容器的一个主要区别就是:vector能进行高效的随机存取,而list做不到这一点
    迭代器部分,掌握这些知识就差不多了。其实迭代器大部分都是用于存取容器中元素的。后续讲解算法的时候还会涉及这些迭代器具体如何在算法中发挥作用。届时再进一步讲解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值