C++:如何循环通过向量中的整数元素

C++: how to loop through integer elements in a vector

本文关键字:向量 元素 整数 何循环 循环 C++      更新时间:2023-10-16

我想在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::setstd::mapstd::unordered_setstd::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(),它们返回类似于上面的beginend指针的迭代器。这是您在本节中需要记住的:在内部,这些迭代器具有指向向量内部缓冲区中元素的指针

一种更简单的迭代方法

理论上,您可以使用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)。因为vectorstd名称空间中,所以它最终调用std::beginstd::end,而这两个名称空间又调用v.begin()v.end()。所有这些机制都是为了确保基于范围的for不仅适用于标准容器,而且适用于任何适当实现beginend的容器。例如,这包括常规的纯数组。

以下是使用迭代器迭代向量的快速代码片段-

#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.