擦除和删除之间的区别

我对std :: removealgorithm的用法有点混淆。 具体来说,我无法理解当我使用这种algorithm时被删除的内容。 我写了一个这样的小testing代码:

std::vector<int> a; a.push_back(1); a.push_back(2); std::remove(a.begin(), a.end(), 1); int s = a.size(); std::vector<int>::iterator iter = a.begin(); std::vector<int>::iterator endIter = a.end(); std::cout<<"Using iter...\n"; for(; iter != endIter; ++iter) { std::cout<<*iter<<"\n"; } std::cout<<"Using size...\n"; for(int i = 0; i < a.size(); ++i) { std::cout<<a[i]<<"\n"; } 

两种情况下的产量都是2,2。

但是,如果我使用擦除删除像这样的东西:

 a.erase(std::remove(a.begin(), a.end(), 1), a.end()); 

我得到的输出为2。

所以我的问题是:

(1)。 是否有任何使用std ::删除除了使用它与擦除function。

(2)。 即使做了std :: remove,为什么a.size()返回2而不是1?

我在Scott Meyer的Effective STL书中读到了关于erase-remove的成语。 但是我仍然有这个困惑。

remove()实际上并没有从容器中删除元素 – 它只是在已删除的元素上分stream未删除的元素。 关键是要认识到, remove()被devise为不仅在一个容器上,而且在任意的前向迭代器对上工作 :这意味着它不能实际删除元素,因为任意的迭代器对不一定有能力删除元素。

例如,指向常规C数组的开始和结束的指针是前向迭代器,因此可以与remove()一起使用:

 int foo[100]; ... remove(foo, foo + 100, 42); // Remove all elements equal to 42 

这里很明显, remove()不能调整数组的大小!

std::remove不会删除实际的对象,而是将它们推到容器的末尾。 内存的实际删除和释放是通过擦除完成的。 所以:

(1)。 是否有任何使用std ::删除除了使用它与擦除function。

是的,它有助于获得一对迭代器到一个新的序列,而不必担心适当的解除分配等

(2)。 即使做了std :: remove,为什么a.size()返回2而不是1?

容器仍然保持这些对象,你只有一套新的迭代器来处理。 因此,大小仍然是以前的样子。

最简单的我可以想出:

erase()是你可以做一个容器中的元素的东西。 给定一个容器的迭代器/索引, erase( it )会从容器中移除迭代器引用的内容。

remove()是你可以对范围做的事情,它重新安排这个范围,但是不会擦除范围内的任何东西。

我面临同样的问题,试图了解其中的差异。 到目前为止所作的解释都是对的,但我只是看了一个例子才明白了。

 #include <algorithm> #include <string> #include <iostream> #include <cctype> int main() { std::string str1 = "Text with some spaces"; std::string::iterator it = remove(str1.begin(), str1.end(), 't'); std::cout << str1 << std::endl;// prints "Tex wih some spaceses" for (str1.begin();it != str1.end(); ++it) { std::cout << *it; //prints "es" } } 

正如你所看到的那样,remove只将小写字母“t”移动到string的末尾,而将新的迭代器返回到新string的末尾(新string是旧string,直到删除的元素被插入)这就是为什么当你打印迭代器,你从“删除”

  "Text with some spaces" ^ ^removes both 't', then shift all elements forward -1 //what we want to remove "Text with some spaces" ^ end of string -2 //original state of string "Tex with some spacess" ^end of string -3 //first 't' removed "Tex wih some spaceses" ^end of string -4 //second 't' removed "Tex wih some spaceses" ^new iterator that remove() returned -5 // the state of string after "remove" and without "erase" 

如果你将从第5步获得的迭代器传递给“erase()”,它将知道从那里擦除到string的末尾,在处理中重新设置string的大小

什么std ::删除做?

这里是std::remove的伪代码。 花几秒钟看看它做了什么,然后阅读解释。

 Iter remove(Iter start, Iter end, T val) { Iter ret = start; while(start != end) { if (*start == val) { start++; //skip } else //copy value *ret++ = *start++; } return ret; } 

请注意,删除只是将序列中的元素向上移动,覆盖要删除的值。 所以你想删除的价值确实没有了,但那有什么问题呢? 假设你的值为{1,2,3,4,5}。 在调用val = 3的remove之后,该vector现在具有{1,2,4,5,5}。 也就是说,4和5被向上移动,以使3从vector中消失,但vector的大小没有改变。 此外,该向量的结尾现在包含5的附加副本。

vector :: erase是做什么的?

std::erase将开始和结束的范围,你想摆脱。 它不会把你想要删除的值,只有范围的开始和结束。 这是伪代码,它是如何工作的:

 erase(Iter first, Iter last) { //copy remaining elements from last while (last != end()) *first++ = *last++; //truncate vector resize(first - begin()); } 

所以擦除操作实际上改变了容器的大小,从而释放了内存。

删除 – 删除成语

std::removestd::erase的组合允许你从容器中删除匹配的元素,这样如果元素被删除,容器实际上会被截断。 以下是如何做到这一点:

 //first do the remove auto removed = std::remove(vec.begin(), vec.end(), val); //now truncate the vector vec.erase(removed, vec.end()); 

这被称为删除 – 删除成语。 为什么它是这样devise的? 洞察力是查找元素的操作是更通用的并且独立于底层容器(仅依赖于迭代器)。 但是,擦除的操作取决于容器如何存储内存(例如,您可能有链接列表而不是dynamic数组)。 所以STL希望容器在提供通用的“移除”操作的同时进行自己的擦除操作,以便所有的容器不必执行该代码。 在我看来,名字是非常误导和std::remove应该被称为std::find_move

注意:以上代码严格伪代码。 实际的STL实现更聪明,例如,使用std::move而不是copy。

删除不“真的”删除任何东西,因为它不能。

为了“实际”从容器中删除元素,您需要访问容器API。 删除工作只与迭代器,而不pipe这些迭代器指向哪些容器。 因此,即使删除要“实际删除”,它不能。

移除覆盖“已移除”的元素,这些元素没有被移除,然后由调用者决定使用返回的新逻辑end而不是原来的end

在你的情况下,从逻辑上删除1vector a,但大小保持到2本身。 擦除实际上从vector中删除了元素。 [从vectornew endold end ]

remove的主要思想是它不能改变元素的数量,它只是按照标准从一个范围中删除元素。