C++中的迭代器是什么?
C++中的迭代器是什么?
迭代器是遍历一组对象的一种方式。通常,它们允许您以类似于使用指针访问经典的C数组的方式按顺序访问STL(标准模板库)容器。要通过迭代器访问对象,您需要像C指针一样取消引用它。要访问集合中的下一个对象,您可以使用递增(++)运算符。某些容器具有多种类型的迭代器,这使您可以以不同的方式遍历集合。
虽然这个问题一开始看起来很明显,但实际上比你意识到的要深刻得多。与 Paul McJones 一起,最初设计迭代器的 Alexander Stepanov 最近出版了一本名为《程序设计元素》(即 EOP)的书。该书第六章全部专门讲述了迭代器,并且该书的其他部分也与迭代器密切相关。任何真正想全面了解迭代器的人都可以考虑阅读这本书。
警告:EOP 不适合心脏脆弱的人。它相对较短(~260页),但非常密集。从经验上讲,开始有点令人不安。我对第一章的最初反应或多或少是“好吧,这么明显的东西几乎不值得一读。毕竟,我上周之前就开始编程了!”
幸运的是,我看了一下练习题,并尝试做了几个——即使我认为这些主题是显而易见的,这些练习也要求进行严格的证明。这有点像被要求(在数学意义上)证明水是湿的。你最终几乎需要阅读该章节几遍才能摆脱自己先入为主的看法,以便查看真正的问题——“湿”到底是什么意思;“湿度”的基本特征是什么?
http://zh.wikipedia.org/wiki/迭代器
迭代器是一种可以让你逐个遍历数组中每个元素的工具。
在C++中,我认为您所说的是 “for_each”...据我所知,C++实际上没有像C#这样的“foreach”,但标准模板库有它。
来自《C++加速》第80页:
迭代器是一个值,它
- 标识容器和容器中的元素
- 允许我们检查存储在该元素中的值
- 提供操作以在容器中移动元素
- 通过限制可用操作的方式,与容器可以有效处理的方式相对应地限制了操作
它们是序列中的位置表示。单独使用时,它们只是些小玩意儿,但当进行解引用时,它们会返回序列中该位置所包含的值。
如果你来自像Java或Python这样的高级语言,你可能已经注意到C++没有任何内置的复杂类型,只有像int
、double
或char
这样的原始类型。由于C++被设计为极其高效,每当你需要使用任何用于保存其他值的集合类型时,创建一个新的自定义类是有意义的。事实上,这就是C++结合低级控制和高级抽象的方法。
标准模板库提供了一个标准化的这些类的集合,你可以使用它们来保存多个值在一个实体下。主要是因为原始的C数组不够灵活,这些容器为开发人员提供了更流畅的开发体验,因为它们:
size()
, upper_bound()
等);目前为止,一切都很顺利。像 vector
或 list
这样的标准容器为开发人员提供了更大的灵活性,而且几乎没有任何性能损失。然而,由于它们是使用 C++ 语义定义的自定义类,因此它们还需要提供一种访问它们所持有的数据的方式。有人可能会认为一个简单的 operator[]
或 next()
方法就可以解决问题,事实上你确实可以这样做,但 STL 借鉴了 C 的思想,创建了一种不依赖于容器对象本身而能够访问容器项的方法:迭代器。
在幕后,迭代器只是一个将指向值的指针包装起来并进行了一些运算符重载的对象。你可以像使用指向数组的指针一样使用迭代器:
std::vector
使用连续的内存块,std::list
在其他地方存储通过指针连接的节点,std::map
使用哈希键在关联数组中),因此迭代器非常有用,可以提供一个通用接口,在每个单独的容器中实现。事实上,这正是允许像std::vector
、std::array
或std::map
这样的容器使用范围for循环枚举的原因:std::vector<int> grades = {4, 5, 1, 8, 10};
for (int grade : grades) std::cout << grade << " ";
//-> 4 5 1 8 10
这个for循环只是使用迭代的语法糖:
std::vector<int> grades = {4, 5, 1, 8, 10};
std::vector::iterator it = grades.begin();
for (; it != grades.end(); ++it) std::cout << grade << " ";
//The output is the same.
你可能已经注意到了一些常见的接口迭代器使用,它们:
*
运算符以解除引用项目;++
运算符以将迭代器向前移动一个项目;==
和!=
运算符以比较它们是否指向相同的值;<container>::iterator
访问它们,尽管您也可以完全将迭代器定义为单独的类)。begin()
方法,返回指向第一个项目的迭代器,以及end()
方法,返回指向最后一个项目之后的迭代器。它指向最后一个项目之后的位置的原因是因为它用于评估迭代器是否已经遍历完所有项目:如果end()
指向了最后一个项目,循环条件将会是it <= grades.end()
。而指向容器下一个位置允许使用简单的小于检查来进行评估,这也是数组从零开始的原因。除此之外,还有rbegin()
和rend()
函数,提供反向迭代器,从末尾到开头,并且其++
运算符实际上是回到开头。
template<typename T, unsgined int Capacity> class Array {
T data[Capacity];
int count;
friend class Iterator;
public:
Array(const std::initializer_list args) {
for (int step = 0; step < args.size(); step++)
data[step] = args[step;
}
int size() : count;
T& operator[](int index) {
if (index < 0 || index > capacity) throw
std::out_of_range("Index out of range.");
return data[index];
}
Iterator<T> begin() {
return &data; //Pointer to array yield the
//address to their first item.
}
Iterator<T> end() {
return &(data + Capacity);
}
};
template<typename T> class Iterator {
T* reference;
public:
Iterator(const Array<T>& array) {
reference = array.begin();
}
T* operator*() {
return reference;
}
Iterator<T> operator++() {
return ++reference; //This is array-specific implementation detail.
}
bool operator!=(const Iterator<T>& other) : *reference != *other;
};
int main() {
Array<int> array = {4, 5, 10, 12, 45, 100};
Iterator<int> it = array.begin();
while (it != array.end()) {
std::cout << *it;
++it;
}
return 0;
}
正如您所看到的,将迭代器作为一个独立的类创建会导致需要单独指定其类型,这就是为什么通常将其定义为容器中的嵌套类的原因。
还有<iterator>
头文件,它为标准迭代器提供了一些有用的功能,例如:
next()
函数,用于增加迭代器;prev()
函数,返回迭代器前一步;advance(int)
函数,将迭代器向前移动n步;如果您需要为自己高度特定的容器编写自定义迭代器,而STL中不存在该容器,则应记住,迭代器用作容器和算法之间的中介,并且对于自己的容器,您应选择适当类型的迭代器(输入、输出、前向、双向或随机访问)来支持一些标准算法。