访问元素中的元素有两种主要方法 std::vector
基于索引的访问
迭代器
这可以通过下标运算符[]或成员函数来完成at()。
两者均会返回对元素中相应位置的引用std::vector(除非是vector<bool>),以便可以读取和修改该元素(如果向量不是const)。
[]和at()不同之处在于,[]它不能保证同时执行任何边界检查at()。访问元素index < 0或index >= size为时未定义行为的元素[],而at()抛出std::out_of_range异常。
注意:为了清楚起见,以下示例使用C ++ 11样式的初始化,但是运算符可用于所有版本(除非标记为C ++ 11)。
std::vector<int> v{ 1, 2, 3 };
// 使用[] int a = v[1]; // 一个是2 v[1] = 4; // v现在包含{1,4,3} // 使用at() int b = v.at(2); // b是3 v.at(2) = 5; // v现在包含{1,4,5} int c = v.at(3); // 引发std :: out_of_range异常
由于该at()方法执行边界检查并可能引发异常,因此它比慢[]。这将成为[]首选代码,在该代码中,操作的语义可确保索引处于范围内。在任何情况下,对向量元素的访问都在固定时间内完成。这意味着访问向量的第一个元素的时间(时间)与访问第二个元素,第三个元素等的代价相同。
例如,考虑此循环
for (std::size_t i = 0; i < v.size(); ++i) { v[i] = 1; }
在这里,我们知道index变量i始终处于界限内,因此i对于每次调用都检查界限是否在CPU周期内是浪费的operator[]。
的front()和back()成员函数允许向量的第一和最后一个元素容易参考存取,分别。这些位置是经常使用的,特殊访问器比使用[]以下方法的替代方法更具可读性:
std::vector<int> v{ 4, 5, 6 }; // 在C ++ 11之前的版本中,这比较冗长 int a = v.front(); // a为4,v.front()等同于v [0] v.front() = 3; // v现在包含{3,5,6} int b = v.back(); // b为6,v.back()等同于v [v.size()-1] v.back() = 7; // v现在包含{3,5,7}
注意:调用front()或back()在空向量上是未定义的行为。您需要empty()在调用front()或之前使用成员函数(用于检查容器是否为空)检查容器是否为空back()。以下是使用'empty()'测试空向量的简单示例:
int main () { std::vector<int> v; int sum (0); for (int i=1;i<=10;i++) v.push_back(i);//创建并初始化向量 while (!v.empty())//循环直到向量测试为空 { sum += v.back();//保持连续运行 v.pop_back();//弹出将其从向量中删除的元素 } std::cout << "total: " << sum << '\n';//将总数输出给用户 return 0; }
上面的示例创建了一个向量,该向量的序列从1到10。然后弹出向量的元素,直到向量为空(使用' empty()')以防止未定义的行为。然后计算向量中数字的总和并显示给用户。
该data()方法返回一个指针,该指针指向用来std::vector内部存储其元素的原始内存。在将向量数据传递给期望使用C样式数组的旧代码时,通常使用此方法。
std::vector<int> v{ 1, 2, 3, 4 }; // v包含{1、2、3、4} int* p = v.data(); // p指向1 *p = 4; // v现在包含{4,2,3,4} ++p; // p指向2 *p = 3; // v现在包含{4,3,3,4} p[1] = 2; // v现在包含{4,3,2,4} *(p + 2) = 1; // v现在包含{4,3,2,1}
在C ++ 11之前,data()可以通过调用front()并获取返回值的地址来模拟该方法:
std::vector<int> v(4); int* ptr = &(v.front()); // or &v[0]
之所以可行,是因为假设矢量的内容不会覆盖unary,则始终可以保证矢量将其元素存储在连续的内存位置中operator&。如果是这样,则必须std::addressof在C ++ 11之前的版本中重新实现。它还假定向量不为空。
迭代器在示例“迭代之上std::vector”和文章迭代器中有更详细的说明。简而言之,它们的作用类似于指向向量元素的指针:
std::vector<int> v{ 4, 5, 6 }; auto it = v.begin(); int i = *it; // 我4岁 ++it; i = *it; // 我5岁 *it = 6; // v包含{4,6,6} auto e = v.end(); // e指向v结束后的元素。可以是 // 用于检查迭代器是否到达向量的末尾: ++it; it == v.end(); // false,它指向位置2处的元素(值6) ++it; it == v.end(); // 真正
std::vector<T>迭代器实际为 T*s与标准是一致的,但是大多数标准库都不这样做。不这样做会改善错误消息,捕获不可移植的代码,并且可用于在非发行版中通过调试检查来对迭代器进行检测。然后,在发行版本中,围绕基础指针的类被优化了。
您可以持久保存对向量元素的引用或指针,以进行间接访问。这些对元素中元素的引用或指针vector保持稳定,并且访问保持定义,除非您在中的元素之前或之后添加/删除元素vector,或者导致vector容量发生变化。这与使迭代器无效的规则相同。
std::vector<int> v{ 1, 2, 3 }; int* p = v.data() + 1; // p指向2 v.insert(v.begin(), 0); // p现在无效,访问* p是未定义的行为。 p = v.data() + 1; // p指向1 v.reserve(10); // p现在无效,访问* p是未定义的行为。 p = v.data() + 1; // p指向1 v.erase(v.begin()); // p现在无效,访问* p是未定义的行为。