从矢量擦除

Erasing from vector

本文关键字:擦除      更新时间:2023-10-16

我试图从向量中删除所有元素。事实上,我写道:

#include<iostream>
#include<vector>
std::vector<int> foo(std::vector<int> v)
{
    std::vector<int> result;
    std::cout << "Initial size = " << v.size() << std::endl;
    for(int i = 0; i < v.size(); i++)
    {
        std::cout << "Size = " << v.size() << std::endl;
        v.erase(v.begin() + i);
    }
    return result;
}
int main()
{
    int a[] = {1 ,2, 5, 8, 213, 2};
    std::vector<int> v;
    v.assign(a, a+6);
    foo(v);
}

演示

为什么该程序会打印

Initial size = 6
Size = 6
Size = 5
Size = 4

在哪里

Size = 3
Size = 2
Size = 1

第三次擦除后,你有 i == 3 和 v.size() == 3 和

退出

你应该学会使用适当的循环来实现你想要实现的目标,或者只是适当地使用它们。当您事先知道需要执行多少次迭代时,可以使用 for 循环,但要注意如何使用索引。

你在循环中所做的是改变向量的大小,所以每次迭代 v.size() 都会变小。当 i == 3 时,向量大小已减小到 3,循环结束时间比预期要早。

对于您想要实现的目标,还有一些替代方案,

// Store the size of the array so it doesn't change midway in the loop
for(int i = 0, iEnd = v.size(); i < iEnd; i++)
{
    std::cout << v.size() << std::endl;
    //v.erase( v.begin() );
    v.pop_back(); // pop_back is quicker but erases from the end
}

// More appropriate, since you don't even need to know the size
while ( !v.empty() ) {
    std::cout << v.size() << std::endl;
    v.pop_back();
}

在这些循环中,我假设您不关心任何特定元素,只想删除它们。如果您确实关心特定元素,其他答案已经提到了这一点。

但实际上你应该使用

v.clear();

因为 STL 已经为您做到了这一点。

要从向量中删除所有元素,请使用std::vector::clear

#include <iostream>
#include <vector>
using namespace std;
int main()
{
    std::vector<int> v(10);
    cout << v.size() << endl;
    v.clear();
    cout << v.size();
    cin.get();
    return 0;
}

当第一个元素被删除时,v.size() 也会更新。因此,当它达到大小 3 时,它的大小也是 3,因此它退出 for 循环。

注意:尽量不要使用更新条件(for 循环的中间条件)作为随着循环进行而变化的东西,除非您确定这样做。

如果你考虑这个抽象循环,你可以很容易地理解这个问题

size_t N = v.size();
size_t i = 0;
size_t j = N;

while ( i < j )
{
   i++;
   j--;
}

因此,您将准确删除向量中的( N + 1 ) / 2元素,或者更准确地说( v.size() + 1 ) / 2元素.:)

要删除向量中的所有元素,您可以使用成员函数clear()

如果你想有选择地删除向量的元素,你可以写

for ( auto it = v.begin(); it != v.end(); )
{
    if ( need_to_delete ) it = v.erase( it );
    else ++it;
}

其中need_to_delete是您想要使用的任何条件。

因为在 3 次擦除之后(当 Size = 4 时),v.size() 是 3,i是 3,所以 i < v.size() 不再成立,你脱离了 for 循环,for(int i = 0; i < v.size(); i++)并从函数返回。

如果只想擦除矢量的所有元素,请尝试 v.clear()

您可能

知道"迭代器失效"的概念,并且您正在使用size_t i来避免这种情况。不幸的是,您正在使用的本质上仍然是一个迭代器,从向量中删除元素会使所有迭代器无效,而不仅仅是:)::iterator类型化迭代器。

invalidate是一个精心挑选的术语,具有细微的细微差别。这意味着迭代器没有停止工作,有时您可能会发现它们仍然有效。但你不应该相信他们。

因此,invalidate并不意味着它们被破坏或错误,而是意味着您应该重新考虑它们。例如,如果您捕获了vec.begin()并导致矢量重新定位其数据,则 vec.begin() 的值可能不再有效。通常不是,但你已经被警告过:)

std::vector 是在一个简单的连续数组上实现的:

[  0  ][  1  ][  2  ][  3  ]

擦除元素时,它包含的对象将被破坏,然后右侧的所有元素都会向左移动,并在内存中物理上重新定位。

[  0  ][~~~~~][  2  ][  3  ] sz=4
[  0  ][  2  <<  2  ][  3  ] sz=4
[  0  ][  2  ][  3  <<  3  ] sz=4
[  0  ][  2  ][  3  ][?????] sz=4

然后向量通过删除的元素计数减少size,在本例中为 1:

[  0  ][  2  ][  3  ] sz=3

你可以看到,当对象不简单或向量很大时,erase()的整个过程是昂贵的,但我会回到这一点。

实现的问题在于您增加了i,并且大小缩小了,因此您最终删除了每隔一个元素。

  i=0
[  0  ][  1  ][  2  ][  3  ] sz=4
erase(i);
  i=0
[~~~~~][  1  ][  2  ][  3  ] sz=3
[  1  <<  1  ][  2  ][  3  ] sz=3
[  1  ][  2  <<  2  ][  3  ] sz=3
[  1  ][  2  ][  3  <<  3  ] sz=3
[  1  ][  2  ][  3  ][?????] sz=3
[  1  ][  2  ][  3  ] sz=3
  i=0
i++;
         i=1
[  1  ][  2  ][  3  ] sz=3
erase(i);
         i=1
[  1  ][~~~~~][  3  ] sz=3
[  1  ][  3  <<  3  ] sz=3
[  1  ][  3  ][?????] sz=3
[  1  ][  3  ] sz=2
         i=1
i++;
               i=2
[  1  ][  3  ] sz=2
break;

std::vector提供了清空数组clear,但如果您有兴趣了解如何自己执行此类操作:

选项 1:从后面删除

while (!vec.empty())
    vec.pop_back();

这样做是破坏最后一个元素,然后减小大小。它非常便宜。

[  0  ][  1  ][  2  ][  3  ] sz=4
pop_back();
[  0  ][  1  ][  2  ][~~~~~] sz=4
[  0  ][  1  ][  2  ] sz=3

选项 2:擦除一系列元素

std::vector<int> vec { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
vec.erase(vec.begin() + 2, vec.begin() + 5);
vec.erase(vec.begin(), vec.end());

选项 3:缩小阵列:

vec.resize(0);

选项 4:清除

这是清空向量的首选和正常方法

vec.clear();