C++ 从向量中删除项时发生Valgrind错误

C++ 从向量中删除项时发生Valgrind错误,c++,c++11,C++,C++11,对你们大多数人来说,这可能是重复的。但我花了这么多时间来解决这个问题。实现了stackoverflow和其他编码站点中给出的许多解决方案。最后我设法修复了它,但我仍然不知道我的旧实现出了什么问题 请帮我看看我的旧代码、新代码、单元测试和valgrind错误,找出导致确切错误的原因 注意: 我在测试单元测试(googletestframework)中的代码 使用C++11编译 m_queue_是一个std::vector 使用谷歌C++编码标准 测试: 队列有2个SAPA项目(由新操作员创建

对你们大多数人来说,这可能是重复的。但我花了这么多时间来解决这个问题。实现了stackoverflow和其他编码站点中给出的许多解决方案。最后我设法修复了它,但我仍然不知道我的旧实现出了什么问题

请帮我看看我的旧代码、新代码、单元测试和valgrind错误,找出导致确切错误的原因

注意:

  • 我在测试单元测试(googletestframework)中的代码
  • 使用C++11编译
  • m_queue_是一个std::vector
  • 使用谷歌C++编码标准
测试:

  • 队列有2个SAPA项目(由新操作员创建)
  • 按id删除第一个项目(队列现在只有一个)
  • 删除唯一的项目 左边是它的id
  • 第二次删除似乎在访问项的m_id_时给出了valgrind错误
这是我的队列项目基类

class Item {
 public:
  Item() {
    type = Type::kInvalid;
  }

  virtual ~Item() {}

  Type type;
  string m_id_ = string("");
};
class SAPA : public Item {
 public:
  SAPA() { Item::type = Type::kSAPA; }
  ~SAPA() {}
};
这是儿童课程

class Item {
 public:
  Item() {
    type = Type::kInvalid;
  }

  virtual ~Item() {}

  Type type;
  string m_id_ = string("");
};
class SAPA : public Item {
 public:
  SAPA() { Item::type = Type::kSAPA; }
  ~SAPA() {}
};
旧代码,用于删除符合特定条件的项目(RemoveIf)。导致VALGRIND问题。

编辑

在你澄清之后,真正的原因似乎很清楚

您将“id”作为
常量字符串传递&
,它传递对原始对象的引用,而不是副本。因为它来自第一个对象,所以我猜在您上面的某个地方有
conststring&id=*m_queue\uu.begin()->m_id
然后继续将其传递给
RemoveIf
。因为比较保证删除第一项,所以在循环中会发生这种情况。现在,
id
是对该项中数据的悬空引用。它在反向迭代版本中起作用的原因是第一项成为您删除的最后一项。删除它后,其他任何内容都不会查看
id
。您可能会将分配
id
的代码行更改为类似
string id=*m_queue.begin()->m_id。通过在此时将其设置为
字符串
而不是
字符串&
,可以强制创建副本。该副本的生存期将一直到作用域结束

结束编辑

您应该注意的一点是std库函数。特别是,您需要
vector::erase()
std::remove_if()
。您想要的擦除版本是使用一对迭代器的版本,而不是一次擦除一个迭代器。当您调用它时,它看起来像
m_queue.erase(XXXX,m_queue.end())
。现在XXXX是什么?如果
,那么它最终是
remove\u的返回值。移除_if的参数是一对迭代器和一元谓词。(即,一个函数,它对向量中的任何内容接受一个
常量T&
,如果应该删除它,则返回true。)返回值是一个迭代器,刚好超过未删除项的末尾

在c++11中,这可能看起来像:

string id = "the_id_to_filter";
m_queue_.erase(std::remove_if(m_queue_.begin(), m_queue_.end(), 
                              [&id](const Item* item) { 
                                  return item_.m_id_ == id;
                              });
在c++11之前的版本中,可以使用以下内容替换lambda:

struct Filter {
   Filter(const string& id) : id_(id) {}
   string id_;
   bool operator()(const Item* item) { return item.m_id_ == id_; }
};
string id = "the_id_to_filter";
m_queue_.erase(std::remove_if(m_queue_.begin(), m_queue_.end(), Filter(id)));
然后你的电话看起来像:

struct Filter {
   Filter(const string& id) : id_(id) {}
   string id_;
   bool operator()(const Item* item) { return item.m_id_ == id_; }
};
string id = "the_id_to_filter";
m_queue_.erase(std::remove_if(m_queue_.begin(), m_queue_.end(), Filter(id)));
如果向量包含
nullptr
或其他不应取消引用的值是有效的,请在谓词函数中添加这些检查。此外,如果向量拥有这些项,您可能希望将其设置为
向量
,而不是
向量
,如果不这样做,则使用擦除(remove_if)习惯用法可能会泄漏。它还使您无需记住删除内容

使用库函数将使您免于因循环中的一个错误而感到沮丧和其他各种痛苦

供参考:


如果(移除(项目,id))
id来自哪里?或者它应该被移除(项目,项目->m_id)
?除了重要的东西,你已经展示了所有东西。什么是“id”?是的,id来自item->m\u id。它通过一个循环,将前面项的id传递给函数。很抱歉丢失了信息。因此,在第一次,它将删除两项中的第一项。下次它将是队列中唯一的项目。谢谢刚刚用你可能的根本原因更新了我的答案。哦,非常感谢你的编辑!我本可以小心使用参考资料的。我现在可以把它标记为答案:)