Effective STL学习笔记-条款32

本文详细解析了C++标准库中的remove算法的工作原理及其局限性,指出remove实际上并不删除元素,而是将待删元素移至容器尾部,并通过示例展示了如何结合erase函数实现真正的元素删除。

如果你真的想删除东西的话就在类似remove的算法后接上 erase

要特别注意标准库的remove的函数它的实现。
这是remove的声明:
template<class ForwardIterator, class T>
ForwardIterator remove(ForwardIterator first, ForwardIterator last,
const T& value);

就像所有算法,remove接收指定它操作的元素区间的一对迭代器。它不接收一个容器,所以remove不知道它作用于哪个容器。此外,remove也不可能发现容器,因为没有办法从一个迭代器获取对应于它的容器。

想想怎么从容器中除去一个元素。唯一的方法是调用那个容器的一个成员函数,几乎都是erase的某个形式,(list有几个除去元素的成员函数不叫erase,但它们仍然是成员函数。)因为唯一从容器中除去一个元素的方
法是在那个容器上调用一个成员函数,而且因为remove无法知道它正在操作的容器,所以remove不可能从一个容器中除去元素。这解释了另一个令人沮丧的观点——从一个容器中remove元素不会改变容器中元素的个数。
所以可以尝试以下代码:

vector<int> vec = { 1,1,2,2,3,3,4,4,5,6 };
cout << "Size:" << vec.size() << endl;  //print
std::remove(vec.begin(), vec.end(), 3);
for (auto item : vec)
{
    cout << "  item:" << item << endl;
}
cout << "Size:" << vec.size() << endl;  //print

从打印可以看出容器的大小完全没有改变见下图,
这里写图片描述
我们可以看出容器大小没变化,但是最后两个元素时5和6,这里我们解释一下为什么会出现这种情况:

  1. remove检测v[0],发现它的值不是要被删除的,然后移动到v[1]。同样的情况发生在v[1]和v[2]。
  2. 发现v[3]应该被删除,所以它记录下v[3]的值应该被覆盖,然后它移动到v[4]。这类似记录v[3]是一个需要填充的“洞”。
  3. 发现v[4]的值应该被保持,所以它把v[4]赋给v[3],记录下v[4]应该被覆盖,然后移动到v[5]。继续类似的压缩,它用v[4]“填充”v[3]而且记录v[4]现在是一个洞。
  4. 以此类推….

remove它返回一个指向最后一个的下一个“不删除的”元素的迭代器。返回值是区间的“新逻辑终点”。所以最后2个元素的由来也是这样。

所以我们一般使用erase配合remove来使用。如下:

vector<int> vec = { 1,1,2,2,3,3,4,4,5,6 };
cout << "Size:" << vec.size() << endl;  //print
auto iter = std::remove(vec.begin(), vec.end(), 3);
vec.erase(iter, vec.end());
for (auto item : vec)
{
    cout << "  item:" << item << endl;
}
cout << "Size:" << vec.size() << endl;  //print

可以得到如下的打印:
这里写图片描述

注意

要搞清这个例子的意思,记住下面这句话:
        remove并不“真的”删除东西,因为它做不到
重复对你有好处:
        remove并不“真的”删除东西, 因为它做不到

但是list容器自带一些成员函数比如:remove、erase、unique(删除邻近的重复值),也可以很方便的真的删除元素。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值