c++向量迭代器与指针

C++ vector iterators vs. pointers

本文关键字:指针 迭代器 向量 c++      更新时间:2023-10-16

给vector的元素赋址的方法有很多。

我可以像这样使用一个指针:

vector<int> v = {10, 11, 12};
int *p = &v[0];
cout << *p;    //Outputs "10"

我也可以这样使用指针:

vector<int> v = {10, 11, 12};
vector<int>::pointer p = v.data();
cout << *p;    //Outputs "10"

也可以使用迭代器类型:

vector<int> v = {10, 11, 12};
vector<int>::iterator i = v.begin();
cout << *i;    //Outputs "10"

我在这里遗漏了什么重要的差异吗?

就能够执行手头的任务而言,它们都工作得一样好。毕竟,它们都提供了满足迭代器要求的对象,并且您使用它们指向vector的相同元素。然而,我会选择vector<int>::iterator选项,因为该类型更能表达我们打算如何使用它。

原始指针类型int*几乎没有告诉您p是什么,除了它存储int的地址。如果孤立地考虑p,它的类型并不能告诉您如何使用它。vector<int>::pointer选项也有同样的问题——它只是把它所指向的对象的类型表示为向量的元素类型。没有理由它实际上需要指向vector

另一方面,vector<int>::iterator告诉你你需要知道的一切。它显式地声明该对象是一个迭代器,并且该迭代器用于指向vector<int>中的元素。

这样做的好处还在于,如果碰巧更改了容器类型,则更容易维护。例如,如果您将指针类型更改为std::list,则指针类型将不再工作,因为元素不再存储为连续数组。容器的iterator类型总是为您提供一个可用于遍历其元素的类型。


当我们有概念时,我希望最佳实践是这样的:

ForwardIteratorOf<int> it = std::begin(v);

其中ForwardIteratorOf<int>(我想象存在)被更改为最能描述您对it意图的任何概念。如果元素的类型无关紧要,则只使用ForwardIterator(或BidirectionalIteratorRandomAccessIterator或其他)。

如果您添加检查:

if ( !v.empty() )

那么,你所展示的所有例子都是同样有效的。

如果您要迭代vector的元素,我会使用:

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

使用迭代器比使用其他形式更容易检查迭代器是否到达vector的末端。

if ( i != v.end() )
{
   // Do stuff.
}

所有这些方法都有其优点,但在本质上它们是非常相似的。当vector为空时,它们中的一些不起作用(它们会导致所谓的"未定义行为")。

根据cppreference:

指向数组元素的指针满足legacyconsecuousiterator

的所有要求

是最强大的迭代器,因为它包含了所有其他迭代器的功能。所以它们可以是同一个,迭代器只是使我们的代码清晰、简洁和可移植的一种手段。

例如我们可以有一个容器"C"…

//template <typename T, int N> class C { //for static allocation
template <typename T> class C {
    //T _data[N]; //for static allocation
    T* _data; //need to dynamically allocate _data
public:
    typedef T* iterator;
}

其中C<int>::iteratorint*,没有区别。

也许我们不需要 legacyconsecuousiterator 的全部功能,所以我们可以重新定义C<int>::iterator作为紧跟在LegacyForwardIterator提纲之后的另一个类。这个新的迭代器类可以重新定义operator*。在这种情况下,它是实现依赖的,当试图访问元素时,int*可能会导致未定义的行为。

这就是为什么应该优先使用迭代器,但在大多数情况下,它们是一样的。

在这两种情况下,我们的容器"C"只要定义所有其他必要的成员函数和类型定义,就可以像其他STL容器一样工作。