C++ 擦除和删除的区别

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/799314/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 17:20:16  来源:igfitidea点击:

Difference between erase and remove

c++stl

提问by Naveen

I am bit confused about the difference between the usage of std::remove algorithm. Specifically I am not able to understand what is being removed when I use this algorithm. I wrote a small test code like this:

我对 std::remove 算法的用法之间的区别有些困惑。具体来说,当我使用此算法时,我无法理解正在删除的内容。我写了一个像这样的小测试代码:

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";
}

The output was 2,2 in both the cases.

在这两种情况下,输出都是 2,2。

However, if I use erase with the remove something like this:

但是,如果我使用擦除与删除类似的东西:

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

I get the output as 2.

我得到的输出为 2。

So my questions are:

所以我的问题是:

(1). Is there any use of std::remove other than using it with erase function.

(1). 除了将 std::remove 与擦除功能一起使用之外,是否还有其他用途。

(2). Even after doing std::remove, why a.size() returns 2 and not 1?

(2). 即使在执行 std::remove 之后,为什么 a.size() 返回 2 而不是 1?

I read the item in Scott Meyer's Effective STL book about the erase-remove idiom. But am still having this confusion.

我在 Scott Meyer 的 Effective STL 书中阅读了有关擦除-删除习语的项目。但我仍然有这种困惑。

回答by j_random_hacker

remove()doesn't actually delete elements from the container -- it only shunts non-deleted elements forwards on top of deleted elements. The key is to realise that remove()is designed to work on not just a container but on any arbitrary forward iterator pair: that means it can'tactually delete the elements, because an arbitrary iterator pair doesn't necessarily have the ability to delete elements.

remove()实际上并没有从容器中删除元素——它只会在已删除元素的顶部分流未删除的元素。关键是要意识到它不仅remove()适用于容器,还适用于任何任意的前向迭代器对:这意味着它实际上不能删除元素,因为任意迭代器对不一定具有删除元素的能力。

For example, pointers to the beginning and end of a regular C array are forward iterators and as such can be used with remove():

例如,指向常规 C 数组开头和结尾的指针是前向迭代器,因此可以用于remove()

int foo[100];

...

remove(foo, foo + 100, 42);    // Remove all elements equal to 42

Here it's obvious that remove()cannot resize the array!

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

回答by dirkgently

std::removedoes not remove the actual objects, rather, pushes them to the end of the container. Actual deletion and deallocation of memory is done via erase. So:

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

(1). Is there any use of std::remove other than using it with erase function.

(1). 除了将 std::remove 与擦除功能一起使用之外,是否还有其他用途。

Yes, it helps to get a pair of iterators to a new sequence without having worry about proper de-allocation etc.

是的,它有助于将一对迭代器获取到新序列,而无需担心适当的取消分配等。

(2). Even after doing std::remove, why a.size() returns 2 and not 1?

(2). 即使在执行 std::remove 之后,为什么 a.size() 返回 2 而不是 1?

The container still holds to those objects, you only have a new set of iterators to work with. Hence the size is still what it used to be.

容器仍然持有这些对象,您只有一组新的迭代器可以使用。因此,大小仍然是以前的大小。

回答by Shital Shah

What does std::remove do?

std::remove 有什么作用?

Here's pseudo code of std::remove. Take few seconds to see whats its doing and then read the explanation.

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

Iter remove(Iter start, Iter end, T val) {
    Iter destination = start;

    //loop through entire list
    while(start != end) { 
        //skip element(s) to be removed
        if (*start == val) { 
            start++; 
         }
         else //retain rest of the elements
             *destination++ = *start++;
     }

     //return the new end of the list
     return destination;
}

Notice that remove simply moved up the elements in the sequence, overwriting values that you wanted to remove. So the values you wanted to remove are indeed gone, but then what's the problem? Let say you had vector with values {1, 2, 3, 4, 5}. After you call remove for val = 3, the vector now has {1, 2, 4, 5, 5}. That is, 4 and 5 got moved up so that 3 is gone from the vector but the size of vectorhasn't changed. Also, the end of the vector now contains additional left over copy of 5.

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

What does vector::erase do?

vector::erase 有什么作用?

std::erasetakes start and end of the range you want to get rid off. It does not take the valueyou want to remove, only start and end of the range. Here's pseudo code for how it works:

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

erase(Iter first, Iter last)
{
    //copy remaining elements from last
    while (last != end())
        *first++ = *last++;

   //truncate vector
   resize(first - begin());
}

So the erase operation actually changes the size of container and so frees up the memory.

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

The remove-erase idiom

remove-erase 成语

The combination of std::removeand std::eraseallows you to remove matching elements from the container so that container would actually get truncated if elements were removed. Here's how to do it:

的组合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());

This is known as remove-erase idiom. Why is it designed like this? The insight is that the operation of finding elements is more generic and independent of underlying container (only dependent on iterators). However operation of erase depends on how container is storing memory (for example, you might have linked list instead of dynamic array). So STL expects containers to do its own erase while providing generic "remove" operation so all containers don't have to implement that code. In my view, the name is very misleading and std::removeshould have been called std::find_move.

这被称为删除-擦除习语。为什么要这样设计?洞察力是查找元素的操作更通用并且独立于底层容器(仅依赖于迭代器)。然而,擦除操作取决于容器如何存储内存(例如,您可能拥有链表而不是动态数组)。因此,STL 期望容器在提供通用“删除”操作的同时进行自己的擦除,因此所有容器都不必实现该代码。在我看来,这个名字非常具有误导性,std::remove应该被称为std::find_move

Note: Above code is strictly pseudocode. The actual STL implementation is more smarter, for example, using std::moveinstead of copy.

注意:以上代码是严格的伪代码。实际的 STL 实现更智能,例如,使用std::move而不是复制。

回答by Molegrammer

i faced the same issue, trying to understand the difference. the explanations that have been give so far are right on the money, but i only understood them after seeing an example;

我遇到了同样的问题,试图了解差异。到目前为止给出的解释是正确的,但我是在看到一个例子后才理解它们的;

#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"
    }

}

as you can see, the remove, only moves the lower case 't' to the end of the string, while returning a new iterator to the end of the new string (new string is the old string up to where the removed element are inserted) this is why when you print the iterator that you got from "remove"

如您所见,remove 仅将小写的 't' 移动到字符串的末尾,同时返回一个新的迭代器到新字符串的末尾(新字符串是插入已删除元素的旧字符串) ) 这就是为什么当您打印从“remove”中获得的迭代器时

   "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"

if you pass the iterator you obtained from step 5 to "erase()" it will know to erase from there to the end of string re-sizing the string in process

如果您将您从第 5 步获得的迭代器传递给“erase()”,它将知道从那里擦除到字符串末尾重新调整字符串大小

回答by Molegrammer

Simplest I can come up with:

我能想到的最简单的:

erase()is something you can do to an element in a container. Given an iterator/index into a container, erase( it )removes the thing the iterator refers to from the container.

erase()是您可以对容器中的元素执行的操作。给定一个容器中的迭代器/索引,erase( it )从容器中删除迭代器所指的东西。

remove()is something you can do to a range, it re-arranges that range but doesn't erase anything from the range.

remove()是您可以对某个范围执行的操作,它会重新安排该范围,但不会从该范围中删除任何内容。

回答by aJ.

remove doesn't "really" remove anything, because it can't.

remove 并没有“真正”删除任何东西,因为它不能。

In order to "actually" remove the elements from container you need to access container APIs. Where as remove works only with iterators irrespective of what containers those iterators points to. Hence, even if remove wants an "actual remove", it can't.

为了“实际”从容器中删除元素,您需要访问容器 API。凡 remove 仅适用于迭代器,而不管这些迭代器指向什么容器。因此,即使 remove 想要“实际删除”,它也不能。

Remove overwrite "removed" elements by the following elements that were not removed and then it is up to the caller to decide to use the returned new logical endinstead of the original end.

Remove overwrite "removed" 由以下未被删除的元素覆盖,然后由调用者决定使用返回的新逻辑end而不是原始end.

In your case remove logically removed 1from vectora but size remained to 2 itself. Erase actually deleted the elements from vector. [ from vector new endto old end]

在您的情况下,1vectora 中删除逻辑上删除但大小保持为 2 本身。Erase 实际上是从 vector 中删除了元素。[从向量new endold end]

The main idea of removeis it cannot change the number of elements and it just remove elements from a range as per criteria.

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

回答by Jayhello

To remove element with some condition(equal some value or other condition like less than) in container like vector, it always combine function member function eraseand std::removeor std::remove_if.

要在像 vector 这样的容器中删除具有某些条件(等于某个值或其他条件(例如小于))的元素,它总是结合函数成员函数eraseand std::removeor std::remove_if

In vector, the function erasecan just delete element by position, like:

在向量中,该函数erase可以按位置删除元素,例如:

iterator erase (iterator position);

迭代器擦除(迭代器位置);

iterator erase (iterator first, iterator last);

迭代器擦除(迭代器第一,迭代器最后);

But if you want to erase elements with some condition, you can combine it with std::removeor std::remove_if.

但是,如果您想删除某些条件的元素,则可以将其与std::remove或结合使用std::remove_if

For example, you want to erase all the elements 6in the below vector:

例如,您要删除6以下向量中的所有元素:

std::vector<int> vec{6, 8, 10, 3, 4, 5, 6, 6, 6, 7, 8};
// std::remove move elements and return iterator for vector erase funtion
auto last = std::remove(vec.begin(), vec.end(), 6);
for(int a:vec)
    cout<<a<<" ";
cout<<endl;
// 8 10 3 4 5 7 8 6 6 7 8 

vec.erase(last, vec.end());
for(int a:vec)
    cout<<a<<" ";
cout<<endl;
// 8 10 3 4 5 7 8 

std::removeworks as below, it does't erase any elements, it just move elements and returns the iterator.

std::remove工作原理如下,它不会擦除任何元素,它只是移动元素并返回迭代器。

enter image description here

在此处输入图片说明

Possible implementation:

可能的实现:

template< class ForwardIt, class T >
ForwardIt remove(ForwardIt first, ForwardIt last, const T& value)
{
    first = std::find(first, last, value);
    if (first != last)
        for(ForwardIt i = first; ++i != last; )
            if (!(*i == value))
                *first++ = std::move(*i);
    return first;
}

Conclusion:

结论:

If you want to remove elements with some condition, you use vector::iterator erase (iterator first, iterator last);essentially.

如果要删除具有某些条件的元素,则vector::iterator erase (iterator first, iterator last);本质上使用。

First get range start:

首先获取范围开始:

auto last = std::remove(vec.begin(), vec.end(), equal_condition_value);

auto last = std::remove(vec.begin(), vec.end(), equal_condition_value);

erase by range(always with end())

按范围擦除(始终与 end())

vec.erase(last, vec.end());

vec.erase(last, vec.end());

cited:

引用:

https://en.cppreference.com/w/cpp/algorithm/remove

https://en.cppreference.com/w/cpp/algorithm/remove