C++ STL 迭代器
STL概述的一个重要特点是数据结构和算法的分离。尽管这是个简单的概念,但这种分离确实使得算法变得非常通用。例如,由于函数是完全通用的,你可以用它来操作几乎任何数据集合,包括链表,容器和数组。
在C++ STL 中,对容器中数据的读和写,是通过迭代器完成的,扮演着容器和算法之间的胶合剂。只要容器提供迭代器的接口,同一套算法代码可以利用在完全不同的容器中,这是抽象思想的经典应用。
迭代器(iterator)是一种检查容器内元素并遍历元素的数据类型。
每种容器类型都定义了自己的迭代器类型。
通过迭代器只能读取容器中的元素,而不能修改。
iterator除了进行++, --操作,可以将iter+n, iter-n赋给一个新的iteraor对象。还可以使用一个iterator减去另外一个iterator。
常见的迭代器类型:
iterator
const_iterator
reverse_iterator
const_reverse_iterator
迭代器的实现原理
迭代器的作用就是提供一个遍历容器内部所有元素的接口。
因此迭代器的内部必须保存一个与容器相关联的指针,然后重载各种运算操作来方便遍历。
实际上这和C++标准库中的智能指针很像。智能指针也是将一个指针封装,然后通过引用计数或是其它方法完成自动释放内存的功能。
为了达到和原有指针一样的功能,也需要对*,->等运算符进行重载。
下面参照智能指针实现了一个简单vector的迭代器。
vecIter主要作用就是包裹一个指针。不同容器内部数据结构不相同,因此迭代器操作符重载的实现也会不同。
比如++操作符,对于线性分配内存的数组来说,直接对指针执行++操作即可。但是如果容器是List就需要采用元素内部的方法,比如ptr->next()之类的方法访问下一个元素。
因此,STL容器都实现了自己的专属迭代器。
templateclass vecIter{ Item *ptr;public: typedef std::forward_iterator_tag iterator_category; typedef Item value_type; typedef Item* pointer; typedef Item& reference; typedef std::ptrdiff_t difference_type;public: vecIter(Item *p = 0) :ptr(p){} Item& operator*()const{ return *ptr; } Item* operator->()const{ return ptr; } //pre vecIter& operator++(){ ++ptr; return *this; } vecIter operator++(int){ vecIter tmp = *this; ++*this; return tmp; } bool operator==(const vecIter &iter){ return ptr == iter.ptr; } bool operator!=(const vecIter &iter){ return !(*this == iter); }};
迭代器特性分类
对于迭代器来说,是否有针对不同特性迭代器的优化方法呢?
答案是肯定的。
拿一个STL算法库中的distance函数来说。distance函数接受两个迭代器参数,然后计算他们两者之间的距离。
显然对于不同的迭代器计算效率差别很大。
比如对于vector容器来说,由于内存是连续分配的。因此指针直接相减即可获得两者的距离。
而list容器是链式表,内存一般都不是连续分配。因此只能通过一级一级调用next()或其他函数,每调用一次再判断迭代器是否相等来计算距离。
vector迭代器计算distance的效率为O(1),而list则为O(n),n为距离的大小。
因此,根据迭代器不同的特性,将迭代器分为5类:
Input Iterator | 所指的对象为只读的。 |
Output Iterator | 所指对象只能进行一次写入操作。 |
Forward Iterator | 允许”读写型”算法在迭代器区间内进行读写操作,比如说replace函数需要读取区间内容,根据所读内容决定是否写入。 |
Bidirectional Iterator | 可双向移动。某些算法需要反向遍历某个迭代器区间。 |
Random Access Iterator | 前四种迭代器只提供部分指针算数能力(前三种支持++运算符,后一种还支持–运算符),第五种则支持所有指针的算术运算,包括p+n, p-n, p[n], p1-p2, p1 |