【问题标题】:How to use correct pointer for find and find_if when dealing with vector处理向量时如何为 find 和 find_if 使用正确的指针
【发布时间】:2014-07-02 04:51:25
【问题描述】:

我有这样的结构:

struct client
{
    string name;
    double money;
};

我还有两个谓词:

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

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

在我的 main 函数中,我使用它来过滤存储在向量 client_list 中的结果(每个人的钱

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);**
}

我注意到,如果我先擦除,然后 find_if,我将失去最后一个客户端。所以我又添加了 1 行来擦除,但是程序崩溃了,因为迭代器现在在末尾,不能擦除。

有没有办法解决这个问题?我想继续使用带有谓词的 find_if 以及像上面那样的 while 循环,因为它们是必需的。

【问题讨论】:

  • 改用std::remove_if 并使用结果调用erase。这就是你要重新发明的轮子。

标签: c++ sorting search


【解决方案1】:

正如其他人所说,std::remove_if 是最好的解决方案。如果 你这样做是出于教学原因(我怀疑这是 情况下,鉴于这些特定的谓词):你在 正确的轨道上。唯一的问题是client_list.erase 使迭代器无效。但是因为它返回一个迭代器 删除元素之后的元素,您可以使用 类似:

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

而且你不想在循环之后调用擦除。迭代器 指定结束,没有要删除的元素。

【讨论】:

    【解决方案2】:

    典型的做法是使用临时向量:

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

    这类似于 Chris 在评论中建议的内容,尽管该解决方案首先将元素移动到向量的末尾,然后从那里截断它们。我不确定这是否不会改变途中的顺序,只需查看文档即可。不过,根据您的需要,任何一个都可以完成工作。


    如果你使用了一个不同的容器,比如 list,它不会使 erase() 中的所有迭代器都失效,你可以这样做:

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

    请注意,如果您调用 erase(),您仍然会使迭代器无效,因此迭代器首先递增,然后使用后缀增量使用前一个值调用 erase()。

    【讨论】:

      【解决方案3】:

      我也同意 chris,使用 std::remove_if:

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

      但如果你想重新发明轮子:

      {
          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); 
          }
      }
      

      【讨论】:

      • 多么扭曲的循环编写方式。 (而且您没有解释 为什么 解决方案不起作用。)
      猜你喜欢
      • 2018-03-19
      • 2015-12-31
      • 1970-01-01
      • 2020-08-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-25
      相关资源
      最近更新 更多