对于vector,为什么选择迭代器而不是指针呢?

For a vector, why prefer an iterator over a pointer?

本文关键字:指针 迭代器 vector 为什么 选择 对于      更新时间:2023-10-16

在Herb Sutter的When Is a Container Not a Container?中,他展示了一个将指针放入容器的示例:

  // Example 1: Is this code valid? safe? good?
  //
  vector<char> v;
  // ...
  char* p = &v[0];
  // ... do something with *p ...

接着是"improvement":

  // Example 1(b): An improvement
  //               (when it's possible)
  //
  vector<char> v;
  // ...
  vector<char>::iterator i = v.begin();
  // ... do something with *i ...

但并没有真正提供一个令人信服的论点:

一般来说,使用迭代器并不是一个坏的准则的指针当你想要指向一个在容器。毕竟,迭代器在大多数情况下都是无效的次数和方法与指针相同,这也是迭代器Exist的目的是提供一种"指向"被包含对象的方法。所以,如果你有选择,更喜欢在容器中使用迭代器。

不幸的是,迭代器不能总是得到相同的效果你可以将指针放入容器中。主要有两种迭代器方法的潜在缺点,当两者都适用时,我们必须继续使用指针:
  1. 在可以使用指针的地方,不能总是方便地使用迭代器。

  2. 使用迭代器可能会产生额外的空间和性能开销,在迭代器是一个对象而不仅仅是一个秃头的情况下指针。

对于vector,迭代器只是一个RandomAccessIterator。对于所有的意图和目的,这是一个指针的薄包装。有一个实现甚至承认了这一点:

   // This iterator adapter is 'normal' in the sense that it does not
   // change the semantics of any of the operators of its iterator
   // parameter.  Its primary purpose is to convert an iterator that is
   // not a class, e.g. a pointer, into an iterator that is a class.
   // The _Container parameter exists solely so that different containers
   // using this template can instantiate different types, even if the
   // _Iterator parameter is the same.

此外,实现存储了_Iterator类型的成员值,该成员值为pointerT*。换句话说,就是一个指针。此外,这种类型的difference_typestd::ptrdiff_t,所定义的操作只是薄包装(即,operator++++_pointer, operator**_pointer),等等。

在Sutter的实参之后,这个迭代器类与指针相比没有任何优点,只有缺点。我说的对吗?

对于向量,在非泛型代码中,你基本上是正确的。

的好处是你可以将RandomAccessIterator传递给一大堆算法,而不管迭代器迭代的是什么容器,也不管该容器是否具有连续存储(因此指针迭代器)。这是一个抽象。

(这个抽象,除其他外,允许实现将基本指针实现替换为一些更有趣的东西,比如用于调试的范围检查迭代器。)

通常认为使用迭代器是一个好习惯,除非你确实不能使用。毕竟,习惯产生一致性,一致性导致可维护性。

迭代器也具有自文档性,而指针则没有。int*指向什么?不知道。std::vector<int>::iterator指的是什么?Aha…

最后,它们提供了一种类型安全措施—虽然这样的迭代器可能只是指针的薄包装,但它们不需要指针:如果迭代器是不同的类型而不是类型别名,那么您就不会意外地将迭代器传递到您不希望它去的地方,或者意外地将其设置为"NULL"。

我同意Sutter的论点和他的大多数其他论点一样有说服力,也就是说,不是很有说服力。

在可以使用指针

的地方不能总是方便地使用迭代器

这是不是的缺点之一。有时候,把指针传递到你不想让它们去的地方实在是太"方便"了。使用单独的类型有助于验证参数。

一些早期的实现对vector::iterator使用了T*,但它导致了各种问题,比如人们不小心将一个不相关的指针传递给vector成员函数。或者将NULL赋值给迭代器

使用迭代器可能会产生额外的空间和性能开销,在迭代器是对象而不仅仅是指针的情况下。

这是在1999年写的,当时我们还认为<algorithm>中的代码应该针对不同的容器类型进行优化。没过多久,每个人都惊讶地发现,编译器自己发现了这个问题。使用迭代器的泛型算法工作得很好!

对于std::vector,绝对没有使用迭代器代替指针的时间开销空间。您发现迭代器类只是指针的薄包装。编译器也会看到它,并生成等价的代码。

选择迭代器而不是指针的一个现实原因是,它们可以在调试构建中作为检查迭代器实现,并帮助您及早发现一些严重的问题。即:

vector<int>::iterator it; // uninitialized iterator
it++;

for (it = vec1.begin(); it != vec2.end(); ++it) // different containers