【问题标题】:C++ vector iterators vs. pointersC++ 向量迭代器与指针
【发布时间】:2014-10-13 03:28:26
【问题描述】:

有很多替代方法来寻址向量的元素。

我可以像这样使用指针:

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"

我在这里遗漏了什么显着差异吗?

【问题讨论】:

  • "addressing elements of a vector",为什么不用最简单的v[0]
  • 除非你需要,否则我会推荐处理向量元素的常用方法
  • @BSH 安全。如果向量为空怎么办?你还有vector::front
  • 中间一个不能通过检查实现来检查正确性(也就是有任何元素),其他两个可以。所有发布模式的实现都未选中。
  • @MFH:迭代器一直是unchecked in Release mode since VS2010

标签: c++ pointers vector iterator


【解决方案1】:

就能够执行手头的任务而言,它们都同样出色。毕竟,它们都提供了一个满足迭代器要求的对象,并且您正在使用它们指向vector 的同一元素。 但是,我会选择 vector&lt;int&gt;::iterator 选项,因为该类型更能表达我们打算如何使用它。

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

另一方面,vector&lt;int&gt;::iterator 告诉你你需要知道的一切。它明确指出该对象是一个迭代器,并且迭代器用于指向 vector&lt;int&gt; 中的元素。

如果您碰巧更改了容器类型,这还具有更易于维护的好处。例如,如果您更改为std::list,则指针类型将不再起作用,因为元素未存储为连续数组。容器的 iterator 类型始终为您提供可用于迭代其元素的类型。


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

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

ForwardIteratorOf&lt;int&gt;(我想象它存在)更改为最能描述您对it 的意图的任何概念。如果元素的类型无关紧要,那么只需 ForwardIterator(或 BidirectionalIteratorRandomAccessIterator 或其他)。

【讨论】:

  • 只是一个小评论:我希望auto 仍然是首选。此外,您可能需要补充一点,vector&lt;int&gt;::iteratorint* 在发布配置中很可能是相同的。
  • @Deduplicator:只有他们永远不会在 VC++ 中
  • std::list 没有随机访问迭代器,因此即使使用迭代器,从 std::vector 更改为 std::list 也可能会出现问题。
  • @MFH vector&lt;int&gt;::iterator 可能不会被声明为int* 的别名,但期望目标代码是相同的(在发布模式下)
【解决方案2】:

如果添加支票:

if ( !v.empty() )

那么,您展示的所有示例都同样有效。

如果您要遍历 vector 的元素,我会选择:

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

使用迭代器比使用其他形式更容易检查迭代器是否已到达向量的末尾。

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

【讨论】:

    【解决方案3】:

    所有这些方式都有其优点,但在核心上它们非常相似。当向量为空时,其中一些不起作用(它们会导致所谓的“未定义行为”)。

    【讨论】:

    • 那么,各自的优势是什么?旁白:UB 上的恐吓报价有什么原因吗?
    • 不是吓人的引号,只是引号,以表明这不仅仅是两个词的组合,而是一个词。否则我也可以写 UB 并留给读者弄清楚这意味着什么。
    【解决方案4】:

    根据cppreference

    指向数组元素的指针满足 LegacyContiguousIterator 的所有要求

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

    例如我们可以有一些容器“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&lt;int&gt;::iterator 将是int*,并且没有区别。

    也许我们不想要/不需要 LegacyContiguousIterator 的全部功能,因此我们可以重新定义 C&lt;int&gt;::iterator 作为另一个遵循 LegacyForwardIterator 大纲的类。这个新的迭代器类可能会重新定义operator*。在这种情况下,它取决于实现,int* 在尝试访问元素时可能会导致未定义的行为。

    这就是为什么应该首选迭代器的原因,但在大多数情况下,它们都是一样的。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-31
      • 1970-01-01
      • 1970-01-01
      • 2013-02-07
      • 1970-01-01
      相关资源
      最近更新 更多