擦除和删除之间的区别
我对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::remove
和std::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
。
在你的情况下,从逻辑上删除1
从vector
a,但大小保持到2本身。 擦除实际上从vector中删除了元素。 [从vectornew end
到old end
]
remove
的主要思想是它不能改变元素的数量,它只是按照标准从一个范围中删除元素。