了解 STL 中的迭代器

Understanding Iterators in the STL

本文关键字:迭代器 STL 了解      更新时间:2023-10-16

C++STL中的迭代器到底是什么?

就我而言,我使用的是list,我不明白为什么您必须创建一个迭代器std::list <int>::const_iterator iElementLocator;才能通过 dereference 运算符显示列表的内容:
cout << *iElementLocator;将其分配给可能list.begin().

请解释迭代器到底是什么以及为什么我必须取消引用或使用它。

STL 中有三个构建块:

  • 器皿
  • 算法
  • 迭代器

在概念级别,容器保存数据。这本身并不是很有用,因为您想对数据一些事情;你想对它进行操作,操纵它,查询它,玩它。算法正是这样做的。但是算法不保存数据,它们没有数据——它们需要一个容器来完成这项任务。将容器交给算法,您就会执行操作。

唯一需要解决的问题是,从技术角度来看,算法如何遍历容器。从技术上讲,容器可以是链表,也可以是数组、二叉树或任何其他可以保存数据的数据结构。但是遍历数组与遍历二叉树不同。尽管从概念上讲,算法想要的只是一次从容器中"获取"一个元素,然后处理该元素,但从容器中获取下一个元素的操作在技术上是非常特定于容器的。

看起来好像

需要为每个容器编写相同的算法,以便每个版本的算法都有正确的代码来遍历容器。但是有一个更好的解决方案:要求容器返回一个可以遍历容器的对象。该对象将具有算法知道的接口。当算法要求对象"获取下一个元素"时,对象将遵守。因为对象直接来自容器,所以它知道如何访问容器的数据。由于对象具有算法知道的接口,因此我们不需要为每个容器复制算法。

这是迭代器。

这里的迭代器将算法粘附到容器上,而不耦合两者。迭代器耦合到容器,算法耦合到迭代器的接口。这里魔力的来源实际上是模板编程。考虑标准copy()算法:

template<class In, class Out>
Out copy(In first, In last, Out res)
{
    while( first != last ) {
        *res = *first;
        ++first;
        ++res;
    }
    return res;
}

copy()算法将类型In上的两个迭代器和一个类型为 Out 的迭代器作为参数。它将从位置 first 开始并在位置 last 之前结束的元素复制到 res 中。该算法知道要获得下一个元素,它需要说 ++first++res .它知道要读取一个元素,它需要说x = *first,写一个元素它需要说*res = x。这是算法假设和迭代器承诺的接口的一部分。如果错误地迭代器不符合接口,那么当类型未定义函数时,编译器将发出错误,以通过类型 InOut 调用函数。

我很懒。所以我不会输入描述迭代器是什么以及如何使用它们,特别是当网上已经有很多文章可以自己阅读时。

以下是我可以首先引用的一些内容,提供完整文章的链接:

MSDN 说,

迭代器是 指针,从其抽象 要求以允许 C++程序与不同的 统一方式的数据结构。 迭代器充当中介 在容器和泛型之间 算法。而不是在 具体的数据类型,算法是 定义为在某个范围内操作 由迭代器类型指定。任何 满足以下条件的数据结构 然后迭代器的要求 由算法操作。那里 是五种类型或类别 迭代器 [...]

顺便说一下,MSDN 似乎已经从标准本身中获取了粗体文本C++特别是来自 §24.1/1 部分,其中

迭代器是 允许C++程序的指针 使用不同的数据结构 (容器(以统一的方式。自 能够构建模板 正常工作的算法和 高效处理不同类型的数据 结构,库形式化不 只是接口,还有 语义和复杂性假设 的迭代器。我支持的所有迭代器 表达式 *i,导致 某个类的值、枚举或 内置类型 T,称为值类型 的迭代器。所有迭代器 i 为 其中表达式 (*i(.m 是 定义明确,支持表达式 具有相同语义的 I->m (*一(.对于每个迭代器,键入 X 用于 定义了哪个平等,有一个 对应有符号积分类型 称为差分类型 迭 代。

cplusplus 说,

在C++中,迭代器是任何对象 那,指向 元素范围(例如数组或 容器(,具有 遍历其中的元素 范围使用一组运算符(在 最小,增量 (++( 和 取消引用 (*( 运算符(。

迭代器最明显的形式是 指针 [...]

您还可以阅读这些:

  • 什么是迭代器?
  • 标准C++库中的迭代器
  • 迭代器(在维基条目中(

请耐心阅读所有这些内容。希望您能对迭代器有所了解,C++。学习C++需要耐心和时间。

迭代器与容器本身不同。迭代器引用容器中的单个项,并提供访问其他项的方法。

考虑设计自己的容器,而不使用迭代器。它可以具有size函数来获取它包含的项目数,并且可以重载[]运算符以允许您按位置获取或设置项目。

但是这种"随机访问"不容易在某些类型的容器上有效实现。如果您获得第 100 万个项目:c[1000000]并且容器内部使用链表,则它必须扫描一百万个项目才能找到您想要的项目。

您可以改为决定允许集合记住"当前"项。它可以具有startnext more等功能,并允许您循环访问内容:

c.start();
while (c.more()) 
{
    item_t item = c.next();
    // use the item somehow
}

但这会将"迭代状态"放在容器中。这是一个严重的限制。如果要将容器中的每个项目与其他每个项目进行比较,该怎么办?这需要两个嵌套循环,都遍历所有项目。如果容器本身存储迭代的位置,则无法嵌套两个这样的迭代 - 内部循环将破坏外部循环的工作。

因此,迭代器是迭代状态的独立副本。您可以开始迭代:

container_t::iterator i = c.begin();

该迭代器i是一个单独的对象,表示容器内的位置。您可以获取存储在该位置的任何内容:

item_t item = *i;

您可以移动到下一项:

i++;

使用某些迭代器,您可以向前跳过几个项目:

i += 1000;

或者获取相对于迭代器标识的位置的某个位置的项目:

item_t item = i[1000];

使用一些迭代器,您可以向后移动。

您可以通过将迭代器与end进行比较来发现您是否超出了容器的内容:

while (i != c.end())

您可以将end视为返回一个迭代器,该迭代器表示超出容器中最后一个位置的位置。

迭代器(以及一般情况下C++(需要注意的重要一点是,它们可能会变得无效。例如,如果您清空容器,通常会发生这种情况:指向该容器中位置的任何迭代器现在都变得无效。在这种状态下,对它们的大多数操作都是未定义的 - 任何事情都可能发生!

迭代器之于STL容器,就像指针之于数组一样。您可以将它们视为指向 STL 容器的指针对象。作为指针,您将能够将它们与指针符号一起使用(例如 *iElementLocatoriElementLocator++(。作为对象,它们将具有自己的属性和方法 (http://www.cplusplus.com/reference/std/iterator(。

已经有很多关于迭代器的良好解释。只是谷歌它。

举个例子。

如果有什么具体的事情你不明白,回来问问。

我建议阅读有关C++运算符重载的信息。这将说明为什么*->基本上可以意味着任何东西。只有这样,您才应该阅读有关迭代器的信息。否则,它可能显得非常混乱。