stl中的remove返回的是什么

转载 2016年08月29日 08:39:35
 

stl中的remove返回的是什么

 254人阅读 评论(0) 收藏 举报
 分类:
 

我将从remove的复习开始这个条款,因为remove是STL中最糊涂的算法。误解remove很容易,驱散所有关于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> v;   // 建立一个vector<int> 用1-10填充它
v.reserve(10);   // (调用reserve的解释在条款14)
for (int i = 1; i <= 10; ++i) {
 v.push_back(i);
}
cout << v.size();   // 打印10
v[3] = v[5] = v[9] = 99;  // 设置3个元素为99
remove(v.begin(), v.end(), 99); // 删除所有等于99的元素
cout << v.size();   // 仍然是10!

要搞清这个例子的意思,记住下面这句话:

remove并不“真的”删除东西,因为它做不到。

重复对你有好处:

remove并不“真的”删除东西,因为它做不到。

remove不知道它要从哪个容器删除东西,而没有容器,它就没有办法调用成员函数,而如果“真的”要删除东西,那就是必要的。

上面解释了remove不做什么,而且解释了为什么它不做。我们现在需要复习的是remove做了什么。

非常简要地说一下,remove移动指定区间中的元素直到所有“不删除的”元素在区间的开头(相对位置和原来它们的一样)。它返回一个指向最后一个的下一个“不删除的”元素的迭代器。返回值是区间的“新逻辑终点”。

 举个例子,这是v在调用remove前看起来的样子:

如果我们把remove的返回值存放在一个叫做newEnd的新迭代器中:

vector<int>::iterator newEnd(remove(v.begin(), v.end(), 99));

这是调用后v看起来的样子:

这里我用问号来标明那些在概念上已经从v中被删除,但继续存在的元素的值。

如果“不删除的”元素在v中的v.begin()和newEnd之间,“删除的”元素就必须在newEnd和v.end()之间——这好像很合理。事实上不是这样!“删除的”值完全不必再存在于v中了。remove并没有改变区间中元素的顺序,所以不会把所有“删除的”元素放在结尾,并安排所有“不删除的”值在开头。虽然标准没有要求,但一般来说区间中在新逻辑终点以后的元素仍保持它们的原值。调用完remove后,在我知道的所有实现中,v看起来像这样:

正如你所见,两个曾经存在于v的“99”不再在那儿了,而一个“99”仍然存在。一般来说,调用完remove后,从区间中删除的值可能是也可能不在区间中继续存在。大多数人觉得这很奇怪,但为什么?你要求remove除去一些值,所以它做了。你并没有要求它把删除的值放在一个你以后可以获取的特定位置,所以它没有做。有问题吗?(如果你不想失去任何值,你可能应该调用partition或stable_partition而不是remove,partition在条款31中描述。)

remove的行为听起来很可恶,但它只不过是算法操作的附带结果。在内部,remove遍历这个区间,把要“删除的”值覆盖为后面要保留的值。这个覆盖通过对持有被覆盖的值的元素赋值来完成。

你可以想象remove完成了一种压缩,被删除的值表演了在压缩中被填充的洞的角色。对于我们的vector v,它按照下面的表演:

  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. 发现v[5]应该被删除,所以忽略并它移动到v[6]。仍然记得v[4]是一个等待填充的洞。
  5. 发现v[6]是一个应该保留的值,所以把v[6]赋给v[4]。记得v[5]现在是下一个要被填充的洞,然后移到v[7]。
  6. 在某种意义上类似上面的,检查v[7]、v[8]和v[9]。把v[7]赋给v[5],v[8]赋给v[6],忽略v[9],因为v[9]的值是要被删除的。
  7. 返回指定下一个要被覆盖的元素的迭代器,在这个例子中这个元素是v[7]。
  8. 正如条款33所解释的,事实上当remove在删除时覆盖的值是指针时,会有重要的影响。但是对于本条款,知道remove不从容器中除去任何元素因为它做不到就够了。只有容器成员函数可以除去容器元素,而那是本条款的整个要点:如果你真的要删除东西的话,你应该在remove后面接上erase。
  9. 你要erase的元素很容易识别。它们是从区间的“新逻辑终点”开始持续到区间真的终点的原来区间的元素。要除去那些元素,你要做的所有事情就是用那两个迭代器调用erase的区间形式(参见条款5)。因为remove本身很方便地返回了区间新逻辑终点的迭代器,这个调用很直截了当:

    vector<int> v;      // 正如从前
    v.erase(remove(v.begin(), v.end(), 99), v.end());  // 真的删除所有
           // 等于99的元素
    cout << v.size();      // 现在返回7
    

    把remove的返回值作为erase区间形式第一个参数传递很常见,这是个惯用法。事实上,remove和erase是亲密联盟,这两个整合到list成员函数remove中。这是STL中唯一名叫remove又能从容器中除去元素的函数:

    list<int> li;   // 建立一个list
        // 放一些值进去
    li.remove(99);   // 除去所有等于99的元素:
        // 真的删除元素,
        // 所以它的大小可能改变了
    

    坦白地说,调用这个remove函数是一个STL中的矛盾。在关联容器中类似的函数叫erase,list的remove也可以叫做erase。但它没有,所以我们都必须习惯它。我们所处于的世界不是所有可能中最好的世界,但却是我们所处的。(附加一点,条款44指出,对于list,调用remove成员函数比应用erase-remove惯用法更高效。)

    一旦你知道了remove不能“真的”从一个容器中删除东西,和erase联合使用就变成理所当然了。你要记住的唯一其他的东西是remove不是唯一这种情况的算法。另外有两种“类似remove”的算法:remove_if和unique。

    remove和remove_if之间的相似性很直截了当。所以我不会细讲,但unique行为也像remove。它用来从一个区间删除东西(邻近的重复值)而不用访问持有区间元素的容器。结果,如果你真的要从容器中删除元素,你也必须成对调用unique和erase,unique在list中也类似于remove。正像list::remove真的删除东西(而且比erase-remove惯用法高效得多)。list::unique也真的删除邻近的重复值(也比erase-unique高效)。

STL Vector remove()和erase()的使用

STL中remove()只是将待删除元素之后的元素移动到vector的前端,而不是删除。若要真正移除,需要搭配使用erase()。 例子如下: #include #include #inc...
  • yockie
  • yockie
  • 2012年08月13日 21:29
  • 34481

stl中的remove返回的是什么

我将从remove的复习开始这个条款,因为remove是STL中最糊涂的算法。误解remove很容易,驱散所有关于remove行为的疑虑——为什么它这么做,它是怎么做的——是很重要的。 这是re...
  • ChuJiangKeDeJiuShu
  • ChuJiangKeDeJiuShu
  • 2014年06月06日 11:44
  • 402

C++中Vector的erase()操作以及与remove的区别

vector v; v.pushback(0); v.pushback(1); v.pushback(1); v.pushback(0); 想要删除值为1的元素 错误代码  vecto...
  • zzuchengming
  • zzuchengming
  • 2016年03月15日 13:05
  • 2273

C++标准库---移除性算法之一remove()&remove_if()

移除性算法         本系列所有算法根据元素值或某一准则,在一个区间内移除某些元素。这些算法并不能改变元素的数量,它们只是以逻辑上的思考,将原本置于后面的“不移除元素”向前移动,覆盖那些被移除...
  • lanzhihui_10086
  • lanzhihui_10086
  • 2014年12月31日 12:26
  • 3574

c++中remove函数

对于remove函数,首先要明白remove函数的实现原理。首先remove函数在STL中的源代码如下: template OutputIterator remove_copy ( Inpu...
  • chenglove1314
  • chenglove1314
  • 2014年09月08日 16:12
  • 3892

STL--vector容器find()函数的用法及UVa10474挖的坑

STL
  • Vapor_
  • Vapor_
  • 2016年11月02日 21:40
  • 832

C++使用STL:慎重选择删除元素的方法

原文出自: C++使用STL:慎重选择删除元素的方法   关于要删除容器中元素要选用什么样的方法(因为不同的容器所支持或删除的方法效率不同),总结如下: 假设容器:Containerc; ...
  • iFuMI
  • iFuMI
  • 2016年05月03日 18:03
  • 1044

vector中的删除,erase和remove的小疑惑--【STL】

对于vector容器的使用,平时只是简单的进行遍历查找一下,未曾进行其它操作,这不,今天出了一点差错; earse方法的操作是将此时的节点删除,然后指向被删除节点的下一个: 如对数据1 6 6 4 7...
  • xjm199
  • xjm199
  • 2014年05月01日 14:29
  • 2000

STL的remove函数和list的remove成员函数

今天看书刚刚看的,就记录下来吧。这可能是老生常谈了,权且作为一个警醒的例子吧。 大家都知道STL有两个非常重要的组成部分,容器和算法。 算法就是一个个的函数,通过迭代器和容器关联在一起,完...
  • whatday
  • whatday
  • 2015年09月16日 22:17
  • 234

STL的remove函数和list的remove成员函数

大家都知道STL有两个非常重要的组成部分,容器和算法。 算法就是一个个的函数,通过迭代器和容器关联在一起,完成一些工作。 算法和容器的分离为程序设计提供了很大的灵活性,但是也带来了一些负面效果,下...
  • XJF199001
  • XJF199001
  • 2016年03月30日 20:02
  • 257
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:stl中的remove返回的是什么
举报原因:
原因补充:

(最多只允许输入30个字)