Effective STL 条款32

STL函数——remove函数 目录1. remove函数2. 错误用法3.remove的执行过程4. 如何真正删除 1. remove函数 remove是STL中的函数,和erase是有区别的: (1)erase是容器自带的成员函数,而remove是STL中的函数; (2)erase是真正的删除,而remove是虚假的删除。(下面会说明) 作用 顾名思义,该函数作用是移除容器中的元素。 参数 参数有三个: 头指针、尾指针和要删除的元素。 int b[] = {1,2,3,4,5,5,6}; vector<int> a 阅读详情

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

我将从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而不是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]。

你可以预想在v中值的移动情况像这样:

正如条款33所解释的,事实上当remove在删除时覆盖的值是指针时,会有重要的影响。但是对于本条款,知道remove不从容器中除去任何元素因为它做不到就够了。只有容器成员函数可以除去容器元素,而那是本条款的整个要点:如果你真的要删除东西的话,你应该在remove后面接上erase。

你要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高效)。

Effective C++ 第三版 所有条款总结及对应代码描述(纯干货) Effective C++ 第三版 所有条款总结及对应代码描述(纯干货) 阅读详情

相关推荐

【绝版C++书籍】《Effective STL》读书笔记

《Effective STL》读书笔记写在前面《Effective STL》中可能过时的内容1 容器第1条:慎重选择容器类型。第2条:不要试图编写独立于容器类型的代码。第3条:确保容器中的对象拷贝正确而高效。第4条:调用empty而不是检查size()是否为0。第5条:区间成员函数优先于与之对应的单元素成员函数。第6条:当心C++编译器最烦人的分析机制。第7条:如果入容器中包含了通过new操作创建的指针,切记在容器对象析构前将delete掉。第8条:切勿创建包含auto_ptr的容器对象。第9条:慎重选择删

Elias_Liu的博客 1757

关于python内置函数remove的返回值为None的问题

1.remove的作用: remove() 函数用于移除列表中某个值的第一个匹配项 2.remove的用法: list.remove(obj) 3.remove的返回值: 没有返回值!!!!!! 4.返回值为None的代码 List = ['1111', '2222', '3333', '4444', '5555'] print(List.remove('1111')) 5.返回值为删除后的列表代码 List = ['1111', '2222', '3333', '4444', '5555'] List

weixin_44553473的博客 6326

ENVI5.6 批量处理GF-2/GF-6/GF-7:从安装到融合的完整自动化流程

本文详细介绍了ENVI5.6环境下批量处理GF-2/GF-6/GF-7卫星数据的完整自动化流程,包括环境配置、工具安装、正射校正、图像融合等关键步骤。通过实战案例和优化技巧,帮助用户高效完成高分卫星影像处理,提升工作效率。特别针对GF-6和GF-7的特殊处理要点进行了深入解析。

weixin_42546392的博客 179

python 列表 remove()函数使用详解

remove() 函数可以删除列表中存在的任意类型的元素。 remove() 函数会遍历列表中的每一个元素,查看是否有元素与之匹配,匹配到相同的元素才会执行删除,这也就意味着 remove() 函数一次只能删除一个元素。

wangyuxiang946的博客 7万+

emwin与Modbus通信结合:项目实例

通过实际项目讲解emwin界面设计与Modbus通信协议的融合应用,深入剖析数据交互流程。掌握emwin在工业控制场景中的实用技巧,提升系统人机交互与稳定通信能力。

weixin_34618526的博客 482

YOLOv5轻量化实战:从Ghost模块到工业级部署的完整指南

本文详细介绍了YOLOv5轻量化实战,重点解析了Ghost模块在工业检测中的应用及其优化策略。通过Ghost卷积技术,模型参数量和GFLOPs显著降低,同时保持高精度,适用于实时性要求严格的工业场景。文章还提供了从模型优化到TensorRT加速的完整部署指南,助力开发者实现高效工业级应用。

weixin_28223453的博客 489

C语言文件操作函数remove、rename、popen、fget

popen()会调用fork()产生子进程,然后从子进程中调用/bin/sh -c来执行参数command的指令。依照此type值,popen()会建立管道连到子进程的标准输出设备或标准输入设备,然后返回一个文件指针。此外,所有使用文件指针(FILE*)操作的函数也都可以使用,除了fclose()以外。如果到达文件末尾或者没有读取到任何字符,str 的内容保持不变,并返回一个空指针。个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。如果错误,则返回 -1,并设置 errno。

weixin_58198422的博客 2608

我用WorkBuddy“克隆“了一个我,从此每句话像我自己说的

各位,做这件事花了多长时间?准备样本可能要半小时,提炼风格十分钟,保存固化再花十分钟。加起来不到一个小时。但这一个小时之后呢?你就拥有了一个"永远在线的文字替身"。以后写口播稿,打开WorkBuddy跟它说;写课程逐字稿,跟它说;写公众号文章,也跟它说。写出来的东西,你的学员一看——“嘿,这不就是翟猛老师说的嘛。说到这儿,可能有人会问:老师,我的风格有那么重要吗?AI写的不都挺好的吗?各位你想想看,你为什么愿意听某个老师的课?不只是因为他讲的内容好,更是因为你喜欢他讲话的方式。

weixin_39284145的博客 816

remove函数使用方法

iterator remove(iterator first, iterator last,val); 1 功能: 找到原容器在[first, last)迭代器范围中的所有等于val的元素,使用后继非val元素进行覆盖(同范围),返回值为last位置的迭代器。 注意:因为是采取覆盖方式,所以原容器中大小和容量都保持不变。未删除的元素的相对顺序将保持不变,而返回的迭代器和原容器最后一个迭代器之间的元素将保持有效。 2 代码示例: #include <vector> #include &

weixin_42542290的博客 3274

别再死记硬背了!用‘小乌龟’实战图解ROS1核心指令(rosnode/rostopic/rosparam保姆级手册)

本文通过经典的turtlesim乌龟仿真器,以可视化实战方式讲解ROS1核心指令(rosnode/rostopic/rosparam),帮助初学者摆脱死记硬背,在操控乌龟的过程中轻松掌握节点管理、话题通信和参数设置等ROS1核心概念。

weixin_28731223的博客 370

粒子群算法Particle Swarm Optimization超详细解析+代码实例讲解

粒子群算法,也称粒子群优化算法或鸟群觅食算法(Particle Swarm Optimization),缩写为 PSO, 是近年来由J. Kennedy和R. C. Eberhart等开发的一种新的进化算法(Evolutionary Algorithm - EA)。

猫敷雪 893

Effective STL学习笔记-条款32

如果你真的想删除东西的话就在类似remove的算法后接上 erase要特别注意标准库的remove的函数它的实现。 这是remove的声明: template<class ForwardIterator, class T> ForwardIterator remove(ForwardIterator first, ForwardIterator last, const T& value);

柠檬柠檬C的博客 421

【QGIS】软件下载安装及GIS4WRF插件使用

【QGIS】软件下载安装及GIS4WRF插件使用

WW、forever的博客 1799

Effective STL 条款3

条款3:使容器里对象的拷贝操作轻量而正确 容器容纳了对象,但不是你给它们的那个对象。此外,当你从容器中获取一个对象时,你所得到的对象不是容器里的那个对象。取而代之的是,当你向容器中添加一个对象(比如通过insert或push_back等),进入容器的是你指定的对象的拷贝。当你从容器中获取一个对象时(比如通过front或back),你取到的是容器中那个对象的拷贝。拷进去,拷出来。这就是STL的方

AMin2001的专栏 1680

【WRFDA实操第一期】服务器中安装 WRFPLUS 和 WRFDA

【WRFDA实操第一期】安装 WRFPLUS 和 WRFDA

WW、forever的博客 2035

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Effective STL 条款39

条款39:用纯函数做判断式我讨厌为你做这些,但我们必须从一个简短的词汇课开始: 判断式(predicate)是返回bool(或者其他可以隐式转化为bool的东西)。判断式在STL中广泛使用。标准关联容器的比较函数是判断式,判断式函数常常作为参数传递给算法,比如find_if和多种排序算法。(排序算法的概览可以在条款31找到。) 纯函数是返回值只依赖于参数的函数。如果f是一个纯

AMin2001的专栏 1965

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

Effective STL 中文版(大全)

候捷说,对于STL,程序员有三个境界,开始是使用STL,然后是理解STL,最后是补充STL。EffectiveSTL是一本非常好的书,帮助你更好的理解STL,其作者就是《EffectiveC++》一书的作者。如果你已经初步了解了STL的容器、迭代器、算法和函数,而又想更好的了解STL,那么《EffectiveSTL》是你的最佳选择。 还有一部分没有找到链接,如果再找不到我会自己试着翻译

幸福村的人 1139

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

Effective STL 条款31

条款31:了解你的排序选择 How can I sort thee? Let me count the ways. 当很多程序员想到排序对象时,只有一个算法出现在脑海:sort。(有些程序员想到qsort,但一旦他们看了条款46,他们会放弃qsort的想法并用sort的想法取代之。) 现在,sort是一个令人称赞的算法,但如果你不需要你就没有必要浪费表情。有时候你不需要完全排序。比如,

AMin2001的专栏 1587

2 Studying《Effective STL》

《Effective STL》摘要 STL是C++标准库的重要组成部分,涵盖容器、迭代器、算法和函数对象等。本文总结了STL使用中的关键要点: 容器选择: 根据插入删除位置选择序列容器(vector、list、deque) 考虑查找效率选择关联容器(set、map)或哈希容器 指针容器需注意资源管理,建议使用智能指针 容器操作优化: 优先使用empty()而非size()==0 区间操作比单元素操作更高效 使用reserve避免频繁重新分配内存 注意string实现的多样性 算法使用: 确保目标区间足够大

u012906122的专栏 753

Effective STL 目录

由于没有看内容,有些翻译可能不准确,请见谅:)容器条款1: 仔细选择你要的容器 条款2: 小心对“容器无关代码”的幻想 条款3: 使容器里对象的拷贝操作轻量而正确 条款4: 用empty来代替检查size是否为0 条款5: 尽量使用范围成员函数代替他们的单元素兄弟 条款6: 警惕C++的及其令人恼怒的分析 条款7: 当使用new得指针的容器时,切记在容器销毁前d

AMin2001的专栏 1483

Effective STL 条款33

条款33:提防在指针的容器上使用类似remove的算法 你在管理一堆动态分配的Widgets,每一个都可能通过检验,你把结果指针保存在一个vector中: class Widget{public: ... bool isCertified() const; // 这个Widget是否通过检验 ...};vector v; // 建立一个vector然后用

AMin2001的专栏 1529

Effective STL目录

    从去年12月开始,我花了40天看了《Effective STL》这本书,并写了相关的读书笔记,我在考虑是否将这部分笔记在BLOG上贴出来。在此之前,先贴上Effective STL目录,并且强烈推荐这本书。如果想加深对STL的了解,这本书必不可少。同时还需要参考的书是《STL源代码剖析》。这篇目录上带有了相关链接。前言致谢导读容器条款1:仔细选择你的容器条款2:小心对“容器无关代码”的幻想

李永亮的专栏 1861

Effective STL 条款34

条款34:注意哪个算法需要有序区间 不是所有算法可以用于任意区间。比如,remove(参见条款32和33)需要前向迭代器和可以通过这些迭代器赋值的能力。所以,它不能应用于由输入迭代器划分的区间,也不能是map或multimap,也不能是set和multiset的一些实现(参见条款22)。同样,很多排序算法(参见条款31)需要随机访问迭代器,所以不可能在一个list的元素上调用这些算法。如果

AMin2001的专栏 1745

《Effective STL》条款3-条款4

条款3使容器里对象的拷贝操作轻量而正确 条款4用empty来代替检查size是否为0

KangRoger的专栏 1440

Effective C++ 条款总结

Effective C++ 条款总结一、让自己习惯C++1 视C++为一个语言联邦2 尽量使用const等替换#define3 尽可能使用const4 确定对象使用前被初始化 自己在看这本书的时候,回去翻看目录的时候,有些规则会被遗忘,因此做个简单的小总结供自己和其他人参考,没读过的还是要先去读一遍的 一、让自己习惯C++ 1 视C++为一个语言联邦 C++是一种包含许多特性的语言,因而不要把它视为一个单一语言。理解C++至少需要学习一下4个部分: C语言。C++仍以C为基础 objected-ori

catkin_ws的博客 1177

【C++】万字详解Effective C++

本文为前段时间读《Effective C++》(侯捷译)时做的个人笔记。

Friday 4289
上一篇: Effective STL 条款31
下一篇: Effective STL 条款33
AMin2001
博客等级 码龄26年 7粉丝 35原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值