有很多替代方法可以解决向量的元素。
我可以像这样使用指针:
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"
我在这里遗失了哪些重大差异?
答案 0 :(得分:5)
就能够完成手头的任务而言,它们都能很好地发挥作用。毕竟,它们都提供了一个满足迭代器要求的对象,并且您使用它们指向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
(或BidirectionalIterator
,RandomAccessIterator
或其他任何内容。
答案 1 :(得分:1)
如果您添加支票:
if ( !v.empty() )
然后,您展示的所有示例都同样有效。
如果您要迭代vector
的元素,我会选择:
vector<int>::iterator i = v.begin();
使用迭代器检查迭代器是否已到达向量的末尾比使用其他形式更容易。
if ( i != v.end() )
{
// Do stuff.
}
答案 2 :(得分:0)
所有这些方式都有其优点,但核心却非常相似。当向量为空时,他们中的一些人不会工作(他们引起所谓的&#34;未定义的行为&#34;)。
答案 3 :(得分:0)
所有功能在您提供的情况下都是相同的。为了提高代码的可读性,迭代器可以更清楚地表明正在对向量执行操作。不过还是有些细微差别,尤其是在const对象方面。
例如
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
const int a[5] = {1, 2, 30, 45, 500};
auto it = begin(a);
it++;
int i = *it;
i++;
cout << i << endl;
} // Prints 3.
另一方面,(不包括标题,以免使屏幕混乱)
int main() {
const int a[5] = {1, 2, 30, 45, 500};
int *p = a;
cout << *p << endl;
}
将不编译。
答案 4 :(得分:0)
根据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<int>::iterator
将是 int*
并且没有区别。
也许我们不想要/不需要 LegacyContiguousIterator 的全部功能,所以我们可以重新定义 C<int>::iterator
作为遵循 LegacyForwardIterator 大纲的另一个类。这个新的迭代器类可能会重新定义 operator*
。在这种情况下,它依赖于实现,并且在尝试访问元素时 int*
可能会导致未定义的行为。
这就是为什么迭代器应该是首选的原因,但在大多数情况下,它们是相同的。
在这两种情况下,只要我们定义所有其他必要的成员函数和 typedef,我们的容器“C”就会像其他 STL 容器一样工作。