为什么需要它!=obj.end() 尽管每个迭代器都应该知道自己何时终止

why does it need it!=obj.end() although every iterator should know itself when to terminate?

本文关键字:迭代器 终止 何时 自己 obj end 为什么      更新时间:2023-10-16

下面的问题刚刚在我脑海中闪过。对于 c++ stl 迭代器,常见的做法是具有如下内容:

for (iterator it=obj.begin(); it!=obj.end(); it++)

我想知道的是,obj.begin(( 实际上可以告诉"它"何时停止,这将使 for 循环看起来像:

for (iterator it=obj.begin(); !it.end(); it++)

好处是使迭代器更加独立,并且可以将(迭代器end(((保存在容器类中。

有时,除了循环访问容器的全部内容之外,您还想执行其他操作。 例如,您可以创建一对迭代器,这些迭代器将仅迭代容器的前半部分。 因此,使用单独的对象来表示结束更加灵活,因为它允许用户更好地控制放置范围末尾的位置。

你是对的,对于迭代所有内容的最常见情况来说,这有点不方便。 但是,C++11 提供了一个基于范围的 for 循环,这使得在整个容器上进行循环变得非常容易,所以就像编程中的许多事情一样,它实际上只是选择正确的结构来最好地表达你的意图的问题。

如果迭代器 API 是这样设计的,指针将不是有效的迭代器(因为指针显然没有 end() (方法。因此,这将排除为具有连续内存的数据结构实现迭代器的最直接方法。

一些库提供"java 风格的迭代器",其工作方式与您描述的类似。

然而,这个方案(hasNext()等(的最大问题是这样的迭代器是类。

例如,STL包含<algorithm>头文件,其中包含std::copystd::generatestd::sortstd::lower_boundstd::fill等函数。所有这些例程都使用开始/结束样式迭代器。因此,您可以将指针与这些函数一起使用。如果这些函数在作为类的迭代器上运行(即,如果它们在内部调用hasNext()而不是!= end()(,那么您将无法将指针传递到 std::sort 等。在这种情况下,您必须将所有内容包装到类中,浪费您的时间,并且失去对<algorithm>的访问权限不值得通过向迭代器类添加atEnd()方法获得小小的便利。这可能就是将迭代器与end()进行比较的原因。

迭代器不需要"知道"容器。它可能只知道并关心内存块或当前节点(或任何适合正在迭代的数据结构(中的偏移量,而不知道包含容器的任何信息。例如,vector迭代器可以作为一个简单的指针实现,而不知道(本身(向量在哪里结束。

此外,STL 算法也需要处理原始指针,因此迭代器需要"模仿"指针。

最终,这只是

STL 发明时(由 Stepanov 在 90 年代初(做出的决定,后来在C++标准化过程中获得批准,迭代器将是指针的泛化。从 http://www.sgi.com/tech/stl/stl_introduction.html:

在反转 C 数组的示例中,要反转的参数是 显然属于double*型.如果你是,有什么论点可以逆转 但是,反转向量还是列表?...答案是 反转的参数是迭代器,它是 指针。

迭代器不必是指针的泛化。C++标准库(以及在此之前的 STL(理论上可以使用不同的迭代模型,其中迭代将由单个迭代器对象或单个范围对象表示,而不是由一对迭代器表示 firstlast

我认为不会有太大的性能差异。现代C++编译器肯定不会有。STL(以及基于它的标准库(始终依赖于编译器体面的内联来提高性能,并且这些类不会比编译器在容器内部已经必须处理的类更糟糕。提供一个简单的包装器,将一对指针转换为迭代器或范围对象也不会有任何重大困难。

有些人确实更喜欢其他迭代器模型 - James Gosling(或者如果不是他,谁设计了Java迭代器(。有些人更喜欢范围(包括大量的C++程序员:因此是Boost.Range(。

我怀疑 STL 的作者和C++喜欢这样一个事实,即 STL 风格的迭代器保留了与 C 的某种兼容性,你可以采用一种 C 算法,该算法使用指针对数组(或用户使用一对指针指定的其他范围(进行操作,并将其几乎不变地转换为使用迭代器对容器(或其他范围(进行操作的C++算法。这种想法意味着你的新语言可以更容易地被现有的用户群吸收,这是C++早期的目标之一。

因此,像"我们能否提供一种测试结束的方法,它缩短了几个字符,代价是使指针不再是迭代器并使迭代器更大",当时可能不会引起太多兴趣。但那是那时,例如安德烈·亚历山德雷斯库(Andrei Alexandrescu(已经敲打了一段时间,在他看来,这不再是最佳选择,而且范围比迭代器更好。

让迭代器以它的方式工作,可以灵活地处理子范围。您不必总是从begin()开始或以end()结束。例如,如果要使用包含特定值的所有迭代器,则可以使用 equal_range() 的返回值作为开始和结束。

D 语言有一个包含迭代器范围的开始和结束的range。它也被提议用于C++:http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/7e52451ed8eea31c

知道迭代器的beginend(以及rbeginrend(在哪里当然是有用的,但你陈述的情况在某种程度上是由 C++11 的基于范围的 for 循环处理的:

for (auto it: obj)
{
    // Do something with *it;
}

在 C++11 中完成工作要容易得多,而无需编写太多样板!