关于C++11 stl中remove函数的讨论
直接上代码:
#include <algorithm>
#include <iterator>
#include <list>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> vec = {1,2,3,3,4,5,6};
// print all elements of the collection
cout<<"pre:";
for(auto it=vec.cbegin();it!=vec.cend();++it){
cout<<*it<<" ";
}
cout<<endl;
cout<<vec.size()<<endl;
// remove all elements with value 3
remove(vec.begin(),vec.end(),3);
// print all elements of the collection
cout<<"post:";
for(auto it=vec.cbegin();it!=vec.cend();++it){
cout<<*it<<" ";
}
cout<<endl;
cout<<vec.size()<<endl;
return 0;
}
输出结果:
pre: 1 2 4 5 6 5 6
7
post:1 2 4 5 6 5 6
7
由于对STL缺乏深层认识,我们一般认为调用remove()后,输出结果应该是:12456
但我们看到这样的话,vector大小变成7。然而调用remove后,前后并不改变vector的大小。
remove()并没有改变集合中的元素数量。cend()返回的是当初那个终点,size()返回的还是当初那个大小。元素次序改变了,有些元素被删除了,值为3的元素被其后的元素覆盖了。
接下来,思考为什么会出现这种情况呢?
先看一段代码:
#include <algorithm>
#include <iterator>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> vec = {1,2,3,3,4,5,6};
vector<int>::iterator end = remove(vec.begin(),vec.end(),3);
//输出vector元素
for(auto it=vec.cbegin();it!=vec.cend();++it){
cout<<*it<<" ";
}
cout<<endl;
cout<<vec.size()<<endl;
cout<<"--------"<<endl;
for(auto it=vec.cbegin();it!=end;++it){
cout<<*it<<" ";
}
cout<<endl;
cout<<vec.size()<<endl;
cout<<"--------"<<endl;
//计算删除的元素个数,即两个迭代器位置的距离
cout<<"number of removed elements: "
<<distance(end,vec.end())<<endl;
return 0;
}
输出结果:
1 2 4 5 6 5 6
7
1 2 4 5 6
7
number of removed elements: 2
看了这段代码,大概就明白了,原来remove()函数返回的是end,即这个end正是“被修改集合”经过元素移除动作后的新的逻辑终点,而cend迭代器还是当初那个终点。
remvove()会把原本置于后面的没有移除的元素向前移动,覆盖被移除的元素。而未被移除的元素在相对次序上保持不变。调用者在调用此算法后,逻辑起点还是原来的begin,而逻辑终点是返回的新逻辑终点,而不是原来的终点end。
另外一种情况:
如果remove删除的是vector最后一个元素呢,出现什么情况?
#include <algorithm>
#include <iterator>
#include <list>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> vec = {1,2,3,3,4,5,6};
// print all elements of the collection
cout<<"pre:";
for(auto it=vec.cbegin();it!=vec.cend();++it){
cout<<*it<<" ";
}
cout<<endl;
cout<<vec.size()<<endl;
// remove all elements with value 3
remove(vec.begin(),vec.end(),6);
// print all elements of the collection
cout<<"post:";
for(auto it=vec.cbegin();it!=vec.cend();++it){
cout<<*it<<" ";
}
cout<<endl;
cout<<vec.size()<<endl;
return 0;
}
输出结果:
pre:1 2 3 3 4 5 6
7
post:1 2 3 3 4 5 6
7
可以看到调用remove()的前后结果是一样的。
解释:remvove()会把原本置于后面的没有移除的元素向前移动,覆盖被移除的元素。由于删除6是最后一个元素,而6下一个元素没有值,就覆盖不了需要被删除的元素6,因此这个需要被删除的6并没有真正被删除掉,vector还保持原样。