stl之remove、erase算法与元素的删除

Effective Stl第32条中强调,remove不是真正意义上的删除,因为它做不到。我们先看一下remove的声明:

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

remove的参数是一对迭代器,指定所要操作的元素区间。但是它并不接受容器作为参数,所以remove并不知道这些元素存放在哪个容器中。并且,remove也不可能推断出容器类型。

所以调用remove并不会使容器元素数量减少。平时我们删除容器元素,首先想到的就是erase方法,但是erase方式是容器的成员方法,这也是stl中除list外删除容器元素的唯一方法。

#include "pch.h"
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
	vector<int> v;
	v.reserve(10);                     /*创建一个容器并初始化大小为10,填入1-10.关于reverse的解释请看我的另一篇博客-stl用法之reverse*/
	for (int i = 1; i <= 10; ++i) {
		v.push_back(i);
	}
	cout << v.size() << endl;          //输出大小,10
	v[3] = v[5] = v[9] = 99;
	remove(v.begin(), v.end(), 99);    //调用remove
	cout << v.size() << endl;          //输出大小,仍然是10
}

运行结果:

以上代码验证了前面所说的,remove并不会删除容器元素。那remove到底做了什么呢?我们先打印一下调用remove之后容器里的元素

简而言之:remove移动了区间中的元素,其结果是,“不用 被删除”的元素移动到了区间的前部,并且保持原来的相对顺序。它返回的迭代器是指向最后一个“不用被删除”的元素之后的元素。这个返回值相当于该区间“新的逻辑结尾”。

就上边的例子,调用remove之前v的布局如下:

调用remove之后,v的布局如下

结论:“不用被删除”的元素在v.begin()和newEnd之间,“需要被删除”的元素在newEnd和v.end()之间。

实际上,对于向量v,remove做了如下的操作:

1.remove检查v[0],看它的值是否需要被删除,接着看v[1],然后是v[2],依次遍历检查

2.检查到v[3],发现它应该被删除,于是它记住v[3]的值要被覆盖,然后移动到v[4]上。这就好比注明了v[3]是一个需要被填充的洞。

3.检查v[4],发现它的值需要保留,所以把v[4]赋给v[3],并记住v[4]需要覆盖。移动到v[5]。

4.移动到v[5],v[5]需要被删除,记录,移动到v[6],此时它记录v[4]、v[5]都是需要被填充。v[6]需要被保留,v[6]赋值给v[4]。

5.用类似的方式检查v[7]v[8]v[9],它吧v7赋给v5,v8赋给v6,忽略v9,因为v9需要被删除。

6.它返回一个迭代器,指向下一个要被覆盖的元素,该例中为v7。

通过以上分析,我们可以得出结论:remove不会删除元素,它只是在移动元素。所以,如果想删除元素,那么必须在remove之后调用erase。

上面例子中,如果我们想删除值为99的元素,可以这样写:

v.erase(remove(v.begin(), v.end(), 99),v.end());

最后,list的成员函数remove也是使用的这种方法删除元素,这是stl中唯一一个名为remove并且确实删除了容器中元素的函数。

#include "pch.h"
#include <iostream>
#include <algorithm>
#include <list>
#include <vector>
using namespace std;
int main() {
	list<int> ll;        /*创建一个容器并初始化大小为10,填入1-10.关于reverse的解释请看我的另一篇博客-stl用法之reverse*/
	for (int i = 1; i <= 10; ++i) {
		ll.push_back(i);
	}
	cout << ll.size() << endl;          //输出大小,10
	ll.remove(9);
	cout << ll.size() << endl;
}

猜你喜欢

转载自blog.csdn.net/weixin_44843859/article/details/109143920