对于向量,为什么更喜欢迭代器而不是指针?

use*_*395 22 c++ pointers iterator vector gotw

在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 ...
Run Code Online (Sandbox Code Playgroud)

然后跟着它"改进":

  // Example 1(b): An improvement
  //               (when it's possible)
  //
  vector<char> v;

  // ...

  vector<char>::iterator i = v.begin();

  // ... do something with *i ...
Run Code Online (Sandbox Code Playgroud)

但实际上并没有提供令人信服的论据:

一般来说,当你想指向一个容器内的对象时,更喜欢使用迭代器而不是指针并不是一个糟糕的指导.毕竟,迭代器在与指针大致相同的时间和相同的方式上无效,并且迭代器存在的一个原因是提供一种"指向"包含对象的方法.因此,如果您有选择,请更喜欢将迭代器用于容器中.

不幸的是,你不能总是得到与迭代器相同的效果,你可以使用指针到容器.迭代器方法有两个主要的潜在缺点,当适用时我们必须继续使用指针:

  1. 您不能总是方便地使用可以使用指针的迭代器.(见下面的例子.)

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

在向量的情况下,迭代器只是一个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.
Run Code Online (Sandbox Code Playgroud)

此外,该实现存储类型的成员值_Iterator,即pointerT*.换句话说,只是一个指针.此外,difference_type对于这种类型而言std::ptrdiff_t,所定义的操作只是薄包装(即,operator++++_pointer,operator**_pointer)等等.

遵循Sutter的论点,这个迭代器类没有提供超越指针的好处,只有缺点.我对么?

Lig*_*ica 25

对于向量,在非通用代码中,您大多是正确的.

好处是你可以将RandomAccessIterator传递给一大堆算法,无论迭代器迭代什么容器,该容器是否具有连续存储(以及指针迭代器).这是一个抽象.

(除其他外,这种抽象允许实现将基本指针实现换成更性感的东西,比如范围检查迭代器以供调试使用.)

除非你真的不能,否则通常认为使用迭代器是个好习惯.毕竟,习惯会产生一致性,并且一致性会导致可维护性.

迭代器也是以指针不是的方式自我记录.有什么int*意义?不知道.有什么std::vector<int>::iterator意义?啊哈...

最后,它们提供了一种类型安全性的度量 - 虽然这样的迭代器可能只是指针周围的瘦包装器,它们不需要指针:如果迭代器是一个独特的类型而不是类型别名,那么你不会意外地通过你的迭代器到你不想去的地方,或者意外地将它设置为"NULL".

我同意Sutter的论点与他的大多数其他论点一样令人信服,即不是很好.


Nem*_*vic 10

优先考虑迭代器而不是指针的一个现实原因是它们可以实现为已检查的迭代器在调试版本中,并帮助您尽早发现一些令人讨厌的问题.即:

vector<int>::iterator it; // uninitialized iterator
it++;
Run Code Online (Sandbox Code Playgroud)

要么

for (it = vec1.begin(); it != vec2.end(); ++it) // different containers
Run Code Online (Sandbox Code Playgroud)


Bo *_*son 10

您不能总是方便地使用可以使用指针的迭代器

不是缺点之一.有时将指针传递到你真正不希望它们去的地方太"方便"了.具有单独的类型有助于验证参数.

一些早期的实现用于T*vector :: iterator,但是它引起了各种各样的问题,例如人们不小心将不相关的指针传递给向量成员函数.或者为迭代器分配NULL.

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

这是在1999年写的,当时我们也认为<algorithm>应该针对不同的容器类型优化代码.不久之后,每个人都惊讶地看到编译器自己想出来了.使用迭代器的通用算法工作得很好!

对于std :: vector,使用迭代器而不是指针绝对没有时间开销.您发现迭代器类只是指针上的一个瘦包装器.编译器也会看到,并生成等效的代码.