简体   繁体   English

处理向量时如何为find和find_if使用正确的指针

[英]How to use correct pointer for find and find_if when dealing with vector

I have a structure like this: 我有这样的结构:

struct client
{
    string name;
    double money;
};

I also have 2 predicates: 我也有2个谓词:

bool less_10(const client& a)
{
    return a.money < 10;
}

bool not_a(const client& a)
{
    return a.name.at(0) != 'A';
}

In my main function I use this to filter out the result stored in vector client_list (everyone with money < 10 (choice 1) or everyone with name not start with A (else)) 在我的主要功能中,我用它来过滤存储在向量client_list中的结果(每个人的钱<10(选择1)或每个名字都不以A开头的人(否则))

if (choice_filter == 1)
    {
        vector<client>::iterator it3;
        it3 = find_if(client_list.begin(), client_list.end(), less_10);
        while (it3 != client_list.end())
        {
            **client_list.erase(it3); 
            it3 = find_if(it3 + 1, client_list.end(), less_10);
        }
        client_list.erase(it3);**
    }

    else
    {
        vector<client>::iterator it4;
        it4 = find_if(client_list.begin(), client_list.end(), not_a);

        while (it4 != client_list.end())
        {
            **client_list.erase(it4);
            it4 = find_if(it4 + 1, client_list.end(), not_a);
        }
        client_list.erase(it4);**
}

I notice that if I erase first, then find_if, i'll lost the last client. 我注意到,如果我先删除,然后再查找,则我将丢失最后一个客户端。 So i added 1 more line to erase, but the program crashes as iterator is now at the end, cant erase. 因此,我又增加了1行要擦除,但是由于迭代器现在位于末尾而无法擦除,因此该程序崩溃了。

Is there any way to get around this? 有什么办法可以解决这个问题? I want to keep using find_if with predicates as well as while loop like above as they are required. 我想继续使用带谓词的find_if以及上面需要的while循环,因为它们是必需的。

As others have said, std::remove_if is the best solution. 正如其他人所说, std::remove_if是最好的解决方案。 If you're doing this for pedagogical reasons (which I suspect is the case, given these particular predicates): you're on the right track. 如果您出于教学原因执行此操作(鉴于这些特定谓词,我怀疑是这种情况):您的方向正确。 The only issue is that client_list.erase invalidates the iterator. 唯一的问题是client_list.erase使迭代器无效。 But since it returns an iterator to the element immediately after the element it erased, you can use something like: 但是,由于它会在删除元素后立即将迭代器返回到该元素,因此您可以使用类似以下内容的方法:

std::vector<Client>::iterator it 
    = std::find_if( client_list.begin(), client_list.end(), predicate );
while ( it != client_list.end() ) {
    it = client_list.erase( it );
    it = std::find_if( it, client_list.end(), predicate );
}

And you don't want to call erase after the loop. 而且您不想在循环后调用擦除。 The iterator designates the end, where there is no element to be erased. 迭代器指定没有要删除的元素的结尾。

I also agree with chris, to using std::remove_if: 我也同意克里斯,使用std :: remove_if:

{
    remove_if(client_list.begin(), client_list.end(), less_10);
}

But if you want to reinvent the wheel: 但是,如果您想重新发明轮子:

{
    vector<client>::iterator it3 = client_list.begin();
    while (true)
    {
        it3 = find_if(it3, client_list.end(), less_10);
        if (it3 == client_list.end()) {
            break;
        }
        it3 = client_list.erase(it3); 
    }
}

The typical way to go is to use a temporary vector: 典型的方法是使用临时向量:

vector<client> tmp;
for (...)
{
    if(predicate(it))
        tmp.push_back(*it);
}
client_list.swap(tmp);

This is similar to what Chris suggested in a comment, although that solution would first move elements to the end of the vector and then truncate them from there. 这类似于克里斯在评论中建议的内容,尽管该解决方案将首先将元素移到向量的末尾,然后从那里截断它们。 I'm not sure if that doesn't change the order on the way, just check the documentation. 我不确定这是否不会改变顺序,只需查看文档即可。 Depending on what you want, either could do the work though. 但是,根据您的需要,两者都可以完成工作。


If you used a different container like list<> that did not invalidate all iterators in erase(), you could do this: 如果您使用了一个不同的容器(例如list <>),而该容器没有使delete()中的所有迭代器无效,则可以这样做:

it = c.begin();
end = c.end();
while(it != end)
{
    if(predicate(*it))
    {
        c.erase(it++);
    }
    else
    {
        ++it;
    }
}

Note that if you call erase(), you invalidate that iterator still, hence the iterator is first incremented and erase() is called with the former value using the postfix increment. 请注意,如果调用Erase(),则会使该迭代器仍然无效,因此该迭代器将首先递增,并使用后缀增量使用前一个值调用Erase()。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM