令人疑惑的 std::remove 算法

摘自《Effective STL》第32条

remove的声明:

1 template<class ForwardIterator, class T>
2 ForwardIterator remove(ForwardIterator first, ForwardIterator last, const T& value);

如同所有的算法一样,remove 也需要一对迭代器来指定所要进行操作的元素区间。它并不接受容器作为参数,所以 remove 并不知道这些元素被存放在哪个容器中。并且,remove 也并不能从迭代器推知对应的容器和容器类型。

唯一可以从容器中删除元素的方法是调用容器的成员函数 erase (list有几个可以删除元素的成员函数,但是没有命名为 erase)。remove 算法并不知道它操作的元素的所在容器,所以不可能从容器中删除元素。

 1 #include <iostream>
 2 #include <memory>
 3 #include <vector>
 4 #include <algorithm>
 5 
 6 using namespace std;
 7 
 8 int main()
 9 {
10     vector<int> c = {1,2,3,4,5,6,7,8,9,1};
11     cout << "size : " << c.size() << endl;
12 
13     remove(c.begin(), c.end(), 1);
14     cout << "size : " << c.size() << endl;
15     
16 }

执行后显示:

size : 10
size : 10

使用 remove 后,容器中的元素并没有减少。

remove 到底做了什么?

简而言之,remove 移动了区间中的元素。其结果是,“需要被删除”的元素被移到了区间的尾部。它返回一个迭代器,指向第一个“需要被删除”的元素。

调用 remove 之前,c 的布局如下:

调用 remove 之后:

vector<int>::iterator newEnd(remove(c.begin(), c.end(), 1));

c 的布局如下

你会发现最后两个元素的值没有发生变化。。。

这个是 remove 算法的附带结果。在内部,remove 遍历整个区间,用需要保留的元素的值覆盖掉那些要被删除的元素的值。这种覆盖是通过对那些需要被覆盖的元素的赋值来完成的。

 

因此,要想删除这些元素,必须调用区间形式的 erase。

还用两个类似的算法:remove_if  和 unique。都需要在调用 remove_if 和 unique 后调用 erase。

其中 list 的调用是不一致的,list::remove 会真正删除元素(并且比使用 erase-remove 习惯用法更加高效),list::unique 也会真正删除元素(并且比使用 erase-unique 更加高效)

 

注意:当容器中存放的是指向动态分配的对象的指针时,应该避免使用 remove 和类似的算法(remove_if 和 unique)。

可以使用智能指针。

posted @ 2016-06-24 10:21  Kjing  阅读(12001)  评论(1编辑  收藏  举报