C++:如何循环通过向量中的整数元素
C++: how to loop through integer elements in a vector
我想在C++中循环遍历向量的元素。
我对这方面很陌生,所以我不太了解细节。
例如:
for (elements in vector) {
if () {
check something
else {
//else add another element to the vector
vectorname.push_back(n)
}
}
我遇到麻烦的是for (vector elements)
。
您通常会使用所谓的基于范围的for循环:
for (auto element : your_vector)
if (condition(element))
// whatever
else
your_vector.push_back(something);
但请注意:在迭代过程中修改向量通常不是一个好主意。如果您的基本概念是在元素还不存在的情况下添加元素,那么您可能需要查找std::set
、std::map
、std::unordered_set
或std::unordered_map
。
为了正确(安全)地执行此操作,您需要了解std::vector
是如何工作的。
矢量容量
你可能知道,向量的工作原理很像一个"无限"大小的数组。也就是说,只要你有足够的内存来容纳它们,它就可以容纳你想要的任何元素。但是它是怎么做到的呢?
向量有一个内部缓冲区(可以把它想象成一个用new
分配的数组),其大小可能与您存储的元素相同,但通常它会更大。它使用缓冲区中的额外空间来插入在使用push_back()
时要插入的任何新元素。
向量的元素数量称为其大小,其<可以容纳的元素数量>可以容纳的元素数量>称为其容量。您可以通过size()
和capacity()
成员函数进行查询。
然而,这个额外的空间必须在某个时刻结束。魔法就在这时发生了:当向量注意到它没有足够的内存来容纳更多的元素时,它会分配一个新的缓冲区,比上一个缓冲区大1,并将所有元素复制到它。这里需要注意的重要一点是新缓冲区将有一个不同的地址。在我们继续解释时,请记住这一点。
迭代器
现在,我们需要讨论迭代器。我不知道你已经学习了多少C++,但想想一个古老的普通数组:
int my_array[5] = {1,2,3,4,5};
您可以通过以下操作获取第一个元素的地址:
int* begin = my_array;
您可以通过以下操作获取数组末尾的地址(更具体地说,是最后一个元素后面的一个):
int* end = begin + sizeof(my_array)/sizeof(int);
如果您有这些地址,迭代数组并打印所有元素的一种方法是:
for (int* it = begin; it < end; ++it) {
std::cout << *it;
}
迭代器的工作原理很像指针。如果您递增它(就像我们在上面使用++it
处理指针一样),它将指向下一个元素。如果你取消引用它(同样,就像我们在上面使用*it
处理指针一样),它将返回它所指向的元素
std::vector
为我们提供了两个成员函数begin()
和end()
,它们返回类似于上面的begin
和end
指针的迭代器。这是您在本节中需要记住的:在内部,这些迭代器具有指向向量内部缓冲区中元素的指针。
一种更简单的迭代方法
理论上,您可以使用std::vector::begin()
和std::vector::end
来迭代这样的向量:
std::vector<int> v{1,2,3,4,5};
for (std::vector<int>::iterator it = v.begin; it != v.end(); ++it) {
std::cout << *it;
}
请注意,除了it
的丑陋类型之外,这与我们的指针示例完全相同。C++引入了关键字auto
,让我们在不需要了解它们的时候摆脱这些丑陋的类型:
std::vector<int> v{1,2,3,4,5};
for (auto it = v.begin; it != v.end(); ++it) {
std::cout << *it;
}
这完全一样(事实上,it
具有完全相同的类型),但现在我们不需要键入(或读取)这种丑陋的东西。
但是,还有更好的方法。C++还引入了基于测距的for
:
std::vector<int> v{1,2,3,4,5};
for (auto it : v) {
std::cout << it;
}
基于射程的for
结构为您做了几件事:
- 它调用
v.begin()
和v.end()
2来获得我们要迭代的范围的上界和下界 - 保留一个内部迭代器(我们称之为
i
),并在循环的每一步调用++i
- 取消对迭代器的引用(通过调用
*i
),并将其存储在it
变量中。这意味着我们不需要自己取消对它的引用(注意std::cout << it
行看起来与其他示例不同)
把它们放在一起
让我们做一个小练习。我们将迭代一个数字向量,对于每个奇数数字,我们将插入一个等于2*n
的新元素。
这是我们最初可能会想到的天真的方式:
std::vector<int> v{1,2,3,4,5};
for (int i : v) {
if (i%2==1) {
v.push_back(i*2);
}
}
当然,这是错误的!矢量v
将以5
的容量开始。这意味着,当我们第一次尝试使用push_back
时,它将分配一个新的缓冲区。
如果缓冲区被重新分配,则其地址已更改。那么,基于范围的用于迭代向量的内部指针会发生什么它不再指向缓冲区!
这就是我们所说的引用无效。请参阅std::vector::push_back
的参考资料。一开始,它说:
如果新的
size()
大于capacity()
,则所有迭代器和引用(包括过去的结束迭代器)都将无效。否则,只有过去的结束迭代器无效。
一旦基于范围的for
尝试增加和取消引用现在无效的指针,就会发生不好的事情。
有几种方法可以避免这种情况。例如,在这个特定的算法中,我知道我们永远不能插入超过n
的新元素。这意味着在循环结束后,矢量的大小永远不会超过2n
。有了这些知识,我可以提前增加矢量的容量:
std::vector<int> v{1,2,3,4,5};
v.reserve(v.size()*2); // Increases the capacity of the vector to at least size*2.
// The code bellow now works properly!
for (int i : v) {
if (i%2==1) {
v.push_back(i*2);
}
}
如果出于某种原因,我不知道特定算法的这些信息,我可以使用一个单独的向量来存储新元素,然后在最后将它们添加到我们的向量中:
std::vector<int> v{1,2,3,4,5};
std::vector<int> doubles;
for (int i : v) {
if (i%2==1) {
doubles.push_back(i*2);
}
}
// Reserving space is not necessary because the vector will allocate
// memory if it needs to anyway, but this does makes things faster
v.reserve(v.size() + doubles.size());
// There's a standard algorithm (std::copy), that, when used in conjunction with
// std::back_inserter, does this for us, but I find that the code bellow is more
// readable.
for (int i : doubles) {
v.push_back(i);
}
最后,是旧的普通for
,使用int
进行迭代。迭代器不能无效,因为它包含一个索引,而不是指向内部缓冲区的指针:
std::vector<int> v{1,2,3,4,5};
for (int i = 0; i < v.size(); ++i) {
if (v[i]%2==1) {
doubles.push_back(v[i]*2);
}
}
希望到现在为止,您已经了解了每种方法的优点和缺点。学习愉快!
1大多少取决于实现。通常,实现会选择分配一个两倍于当前缓冲区大小的新缓冲区。
这是一个小小的谎言。整个故事有点复杂:它实际上试图调用begin(v)
和end(v)
。因为vector
在std
名称空间中,所以它最终调用std::begin
和std::end
,而这两个名称空间又调用v.begin()
和v.end()
。所有这些机制都是为了确保基于范围的for
不仅适用于标准容器,而且适用于任何适当实现begin
和end
的容器。例如,这包括常规的纯数组。
以下是使用迭代器迭代向量的快速代码片段-
#include<iostream>
#include<iterator> // for iterators to include
#include<vector> // for vectors to include
using namespace std;
int main()
{
vector<int> ar = { 1, 2, 3, 4, 5 };
// Declaring iterator to a vector
vector<int>::iterator ptr;
// Displaying vector elements using begin() and end()
cout << "The vector elements are : ";
for (ptr = ar.begin(); ptr < ar.end(); ptr++)
cout << *ptr << " ";
return 0;
}
文章阅读更多-使用';对于';环。希望它能有所帮助。
试试这个,
#include<iostream>
#include<vector>
int main()
{
std::vector<int> vec(5);
for(int i=0;i<10;i++)
{
if(i<vec.size())
vec[i]=i;
else
vec.push_back(i);
}
for(int i=0;i<vec.size();i++)
std::cout<<vec[i];
return 0;
}
输出:
0123456789
Process returned 0 (0x0) execution time : 0.328 s
Press any key to continue.
- 向量元素的引用地址与它所指向的向量元素的地址不同.为什么
- 在 C++ 中输出枚举类类型的向量元素
- 用向量的向量元素初始化向量的空向量
- 为什么不允许使用可变长度数组作为向量元素?
- 在函数中传递向量元素
- 使用 Rcpp 加速替换迭代算法中的列表和向量元素是否合法?
- 返回向量元素的 l 值的正确方法是什么?
- 当向量增长时,指向向量元素的C++指针是否无效
- 如何在C++中从一个向量元素减去std::unorderede_map向量元素并更新它
- C++:向量元素赋值导致访问冲突
- 将向量元素与字符串元素进行比较,而不初始化向量
- 从2D矢量中找出最小尺寸的向量元素的更好方法
- 当我尝试将一个向量元素的值分配给另一个向量元素时,为什么我的应用程序会崩溃
- 使用递归C++向量元素的总和
- 如何查找为数组中的最低值编制索引的向量元素
- 限制指针访问STL ::向量元素
- 访问索引指向向量元素时的seg故障 - 做错了什么
- 如何使用指针访问向量元素的各个元素
- 向量元素数据损坏了Find()操作
- 将字符值分配给向量元素.C