我对std :: remove算法的使用之间的区别感到有点困惑.具体来说,我无法理解使用此算法时要删除的内容.我写了一个像这样的小测试代码:
std::vector<int> a;
a.push_back(1);
a.push_back(2);
std::remove(a.begin(), a.end(), 1);
int s = a.size();
std::vector<int>::iterator iter = a.begin();
std::vector<int>::iterator endIter = a.end();
std::cout<<"Using iter...\n";
for(; iter != endIter; ++iter)
{
std::cout<<*iter<<"\n";
}
std::cout<<"Using size...\n";
for(int i = 0; i < a.size(); ++i)
{
std::cout<<a[i]<<"\n";
}
Run Code Online (Sandbox Code Playgroud)
两种情况下的输出均为2,2.
但是,如果我使用erase删除这样的东西:
a.erase(std::remove(a.begin(), a.end(), 1), a.end());
Run Code Online (Sandbox Code Playgroud)
我得到输出为2.
所以我的问题是:
(1).有没有使用std :: remove而不是使用擦除功能.
(2).即使在执行std :: remove之后,为什么a.size()返回2而不是1?
我在Scott Meyer的Effective STL书中读到了关于擦除删除习语的内容.但我仍然有这种困惑.
j_r*_*ker 52
remove()实际上并没有从容器中删除元素 - 它只在已删除的元素之上分流未删除的元素.关键是要意识到它不仅remove()适用于容器,而且适用于任意前向迭代器对:这意味着它实际上不能删除元素,因为任意迭代器对不一定能够删除元素.
例如,指向常规C数组的开头和结尾的指针是前向迭代器,因此可以用于remove():
int foo[100];
...
remove(foo, foo + 100, 42); // Remove all elements equal to 42
Run Code Online (Sandbox Code Playgroud)
这里很明显remove()无法调整数组的大小!
dir*_*tly 17
std::remove不删除实际对象,而是将它们推送到容器的末尾.通过擦除实现内存的实际删除和释放.所以:
(1).有没有使用std :: remove而不是使用擦除功能.
是的,它有助于将一对迭代器添加到新序列中,而无需担心正确的解除分配等.
(2).即使在执行std :: remove之后,为什么a.size()返回2而不是1?
容器仍然保存到这些对象,您只有一组新的迭代器可供使用.因此,尺寸仍然是以前的样子.
Shi*_*hah 15
std :: remove有什么作用?
这是伪代码std::remove.花几秒钟看看它做了什么,然后阅读解释.
Iter remove(Iter start, Iter end, T val) {
Iter destination = start;
//loop through entire list
while(start != end) {
//skip element(s) to be removed
if (*start == val) {
start++;
}
else //retain rest of the elements
*destination++ = *start++;
}
//return the new end of the list
return destination;
}
Run Code Online (Sandbox Code Playgroud)
请注意,删除只是向上移动序列中的元素,覆盖您要删除的值.所以你要删除的值确实消失了,但那么问题是什么?假设你有值为{1,2,3,4,5}的向量.在为val = 3调用remove后,向量现在具有{1,2,4,5,5}.也就是说,4和5向上移动,使得3从向量中消失,但向量的大小没有改变.此外,向量的末尾现在包含5的额外剩余副本.
vector :: erase有什么作用?
std::erase开始和结束您想要摆脱的范围.它不会删除您想要删除的值,只需要开始和结束范围.这是它的工作原理的伪代码:
erase(Iter first, Iter last)
{
//copy remaining elements from last
while (last != end())
*first++ = *last++;
//truncate vector
resize(first - begin());
}
Run Code Online (Sandbox Code Playgroud)
因此擦除操作实际上会改变容器的大小,从而释放内存.
删除 - 删除成语
的组合std::remove,并std::erase允许您从容器中取出匹配的元素,使容器实际上将被截断,如果内容被删除.这是怎么做的:
//first do the remove
auto removed = std::remove(vec.begin(), vec.end(), val);
//now truncate the vector
vec.erase(removed, vec.end());
Run Code Online (Sandbox Code Playgroud)
这被称为删除擦除习语.为什么这样设计?洞察力是查找元素的操作更通用且独立于底层容器(仅依赖于迭代器).但是擦除操作取决于容器如何存储内存(例如,您可能有链表而不是动态数组).因此STL希望容器在提供通用的"删除"操作时自行擦除,因此所有容器都不必实现该代码.在我看来,该名称非常具有误导性,std::remove应该被称为std::find_move.
注意:上面的代码是严格的伪代码.实际的STL实现更加智能,例如,使用std::move而不是复制.
Jay*_*llo 11
要在向量等容器中删除具有某些条件(等于某个值或其他条件,如小于)的元素,它总是结合函数成员函数erase和std::remove或std::remove_if。
在向量中,该函数erase只能按位置删除元素,例如:
迭代器擦除(迭代器位置);
迭代器擦除(迭代器第一个,迭代器最后一个);
但如果你想删除符合某种条件的元素,你可以将它与std::removeor结合起来std::remove_if。
例如,您想要删除6以下向量中的所有元素:
std::vector<int> vec{6, 8, 10, 3, 4, 5, 6, 6, 6, 7, 8};
// std::remove move elements and return iterator for vector erase funtion
auto last = std::remove(vec.begin(), vec.end(), 6);
for(int a:vec)
cout<<a<<" ";
cout<<endl;
// 8 10 3 4 5 7 8 6 6 7 8
vec.erase(last, vec.end());
for(int a:vec)
cout<<a<<" ";
cout<<endl;
// 8 10 3 4 5 7 8
Run Code Online (Sandbox Code Playgroud)
std::remove工作原理如下,它不会删除任何元素,它只是移动元素并返回迭代器。
可能的实施:
template< class ForwardIt, class T >
ForwardIt remove(ForwardIt first, ForwardIt last, const T& value)
{
first = std::find(first, last, value);
if (first != last)
for(ForwardIt i = first; ++i != last; )
if (!(*i == value))
*first++ = std::move(*i);
return first;
}
Run Code Online (Sandbox Code Playgroud)
结论:
如果你想删除符合某些条件的元素,你可以使用vector::iterator erase (iterator first, iterator last); 本质。
首先获取范围开始:
自动最后 = std::remove(vec.begin(), vec.end(), equal_condition_value);
按范围擦除(始终使用 end())
vec.erase(最后一个, vec.end());
引用:
https://en.cppreference.com/w/cpp/algorithm/remove
小智 6
最简单的我可以想出:
erase()是你可以对容器中的元素做的事情.给定容器中的迭代器/索引,erase( it )删除迭代器从容器引用的东西.
remove() 是你可以对范围做的事情,它重新安排范围,但不会删除范围内的任何东西.
我面临同样的问题,试图了解其中的差异.到目前为止给出的解释是正确的,但我只是在看到一个例子之后理解它们;
#include <algorithm>
#include <string>
#include <iostream>
#include <cctype>
int main()
{
std::string str1 = "Text with some spaces";
std::string::iterator it = remove(str1.begin(), str1.end(), 't');
std::cout << str1 << std::endl;// prints "Tex wih some spaceses"
for (str1.begin();it != str1.end(); ++it)
{
std::cout << *it; //prints "es"
}
}
Run Code Online (Sandbox Code Playgroud)
如你所见,remove,只将小写't'移动到字符串的末尾,同时将新的迭代器返回到新字符串的末尾(new string是旧字符串,直到插入删除元素的位置)这就是为什么当你打印从"删除"获得的迭代器
"Text with some spaces"
^ ^removes both 't', then shift all elements forward -1 //what we want to remove
"Text with some spaces"
^ end of string -2 //original state of string
"Tex with some spacess"
^end of string -3 //first 't' removed
"Tex wih some spaceses"
^end of string -4 //second 't' removed
"Tex wih some spaceses"
^new iterator that remove() returned -5 // the state of string after "remove" and without "erase"
Run Code Online (Sandbox Code Playgroud)
如果你将从步骤5获得的迭代器传递给"erase()",它将知道从那里擦除字符串的结尾重新调整字符串的大小