从矢量擦除
Erasing from vector
我试图从向量中删除所有元素。事实上,我写道:
#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();
- 引用一个已擦除类型(void*)的指针
- 擦除while循环中迭代的元素
- 在运行时处理类型擦除的数据-如何不重新发明轮子
- C++擦除(如果存在)
- 在映射擦除c++期间执行循环的次数
- 为什么擦除方法会影响结束方法
- C++ 字符串类擦除成员函数的时空复杂性
- 类型擦除的std::function与虚拟函数调用的开销
- C++14 中unordered_map矢量和擦除删除成语的奇怪行为
- C++ 擦除函数中需要澄清
- 循环挂起迭代的 std::擦除 on std::list
- 擦除许多矢量元素,同时使用'auto'
- 如何擦除冗余输入?
- C++ STL 设置按值擦除
- 如何在C++中允许成员函数的自定义返回类型进行类型擦除?
- 为什么我的向量::擦除调用会抛出"vector subscript out of range"?
- 如何在写入文件时擦除最后一个逗号
- C++:std::ofstream 方法 open() 在第二次迭代时擦除打开的 ifstream 文件
- 双链表的擦除值函数,未知错误
- 如何编写用于多映射插入和擦除功能的 API?