gpt4 book ai didi

c++ - 从 vector 中删除项目时出现 Valgrind 错误

转载 作者:塔克拉玛干 更新时间:2023-11-03 02:00:18 25 4
gpt4 key购买 nike

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

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

注意:

  • 我正在通过单元测试(Google 测试框架)测试我的代码。
  • 使用 C++11 编译
  • m_queue_ 是一个 std::vector
  • 使用 Google C++ 编码标准

测试:

  • 队列有 2 个 SAPA 项目(由新运算符(operator)创建)
  • 通过 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() {}
};

旧代码用于在项目满足特定条件 (RemoveIf) 时将其删除。导致 VALGRIND 问题。

This was proposed as a correct way to remove items from a vector in many posts.

void Queue::RemoveItems(const string& id) const {
vector<Item*>::iterator it = m_queue_.begin();
while (it != m_queue_.end()) {
Item* item = *it;
if (item == nullptr) {
continue;
}

if (RemoveIf(item, id)) {
delete item;
item = nullptr;
it = m_queue_.erase(it);
} else {
++it;
}
}
}

RemoveIf 函数

bool Queue::RemoveIf(Item* item,
const string& id) const {
**cout << id.c_str() << endl; <--- seems to cause the invalid read**

if (item->m_id_.compare(id) == 0) {
return true;
}

return false;
}

VALGRIND 输出显示大小为 8 的读取无效。抱歉,这包含一些项目特定名称。

> ==21919== Invalid read of size 8
> ==21919== at 0x5880B90: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::c_str() const (in
> /usr/lib64/libstdc++.so.6.0.21)
> ==21919== by 0xEC416C: Queue::RemoveIf(network::multiplexer::Item*, blf::String const&) const (network_multiplexer_queue.cc:99)
> ==21919== by 0xEC3FFB: Queue::RemoveItems(blf::String const&) const (network_multiplexer_queue.cc:85)
> ==21919== by 0xEC4FDC: Queue::OnTimer() const (network_multiplexer_queue.cc:228)
> ==21919== by 0xFB05E0: (anonymous namespace)::NetworkMultiplexerTest_sapaTimeout_shouldBeHandled_successfully_Test::TestBody()
> (network_multiplexer_comm_test.cc:1201)
> ==21919== by 0x1232186: void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test,
> void>(testing::Test*, void (testing::Test::*)(), char const*) (in
> /home/sajith/cioffi/cioffi-linux/build/unit_tests)
> ==21919== by 0x122C547: void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test,
> void>(testing::Test*, void (testing::Test::*)(), char const*) (in
> /home/sajith/cioffi/cioffi-linux/build/unit_tests)
> ==21919== by 0x12124B7: testing::Test::Run() (in /home/sajith/cioffi/cioffi-linux/build/unit_tests)
> ==21919== by 0x1212D99: testing::TestInfo::Run() (in /home/sajith/cioffi/cioffi-linux/build/unit_tests)
> ==21919== by 0x1213444: testing::TestCase::Run() (in /home/sajith/cioffi/cioffi-linux/build/unit_tests)
> ==21919== by 0x1219F2E: testing::internal::UnitTestImpl::RunAllTests() (in
> /home/sajith/cioffi/cioffi-linux/build/unit_tests)
> ==21919== by 0x1233583: bool testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl,
> bool>(testing::internal::UnitTestImpl*, bool
> (testing::internal::UnitTestImpl::*)(), char const*) (in
> /home/sajith/cioffi/cioffi-linux/build/unit_tests)
> ==21919== Address 0x6d24a00 is 16 bytes inside a block of size 112 free'd
> ==21919== at 0x4C2A131: operator delete(void*) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
> ==21919== by 0xED3991: SAPA::~SAPA() (network_multiplexer_queue_item.h:82)
> ==21919== by 0xEC4045: Queue::RemoveItems(blf::String const&) const (network_multiplexer_queue.cc:86)
> ==21919== by 0xEC4FDC: OnTimer() const (network_multiplexer_queue.cc:228)
> ==21919== by 0xFB05E0: (anonymous namespace)::NetworkMultiplexerTest_sapaTimeout_shouldBeHandled_successfully_Test::TestBody()
> (network_multiplexer_comm_test.cc:1201)
> ==21919== by 0x1232186: void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test,
> void>(testing::Test*, void (testing::Test::*)(), char const*) (in
> /home/sajith/cioffi/cioffi-linux/build/unit_tests)
> ==21919== by 0x122C547: void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test,
> void>(testing::Test*, void (testing::Test::*)(), char const*) (in
> /home/sajith/cioffi/cioffi-linux/build/unit_tests)
> ==21919== by 0x12124B7: testing::Test::Run() (in /home/sajith/cioffi/cioffi-linux/build/unit_tests)
> ==21919== by 0x1212D99: testing::TestInfo::Run() (in /home/sajith/cioffi/cioffi-linux/build/unit_tests)
> ==21919== by 0x1213444: testing::TestCase::Run() (in /home/sajith/cioffi/cioffi-linux/build/unit_tests)
> ==21919== by 0x1219F2E: testing::internal::UnitTestImpl::RunAllTests() (in
> /home/sajith/cioffi/cioffi-linux/build/unit_tests)
> ==21919== by 0x1233583: bool testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl,
> bool>(testing::internal::UnitTestImpl*, bool
> (testing::internal::UnitTestImpl::*)(), char const*) (in
> /home/sajith/cioffi/cioffi-linux/build/unit_tests)

下面修复了 valgrind 问题这是向后迭代并删除项目的新代码。

auto it = m_queue_.end();
while (it > m_queue_.begin()) {
it--;
Item* item = *it;
if (item == nullptr) {
continue;
}

if (RemoveIf(item, id)) {
delete item;
item = nullptr;
it = m_queue_.erase(it);
}
}

最佳答案

编辑

经过你的澄清,真正的原因似乎已经很清楚了。

您将“id”作为 const string& 传递它传递对原始对象的引用而不是拷贝。因为它来自第一个物体,我猜你上面的某个地方有 const string& id = *m_queue_.begin()->m_id_;然后您继续将其传递给 RemoveIf .因为比较肯定会删除第一项,所以这发生在您的循环中。现在id是对该项目中数据的悬挂引用。它在您的反向迭代版本中起作用的原因是第一项成为您删除的最后一项。删除它之后,没有其他东西看id .您可能会更改分配 id 的代码行像string id = *m_queue_.begin()->m_id_; .通过将其设为 string而不是 string&那时,您强制制作拷贝。该拷贝将有生命周期到范围结束。

结束编辑

您应该关注的一件事是标准库函数。特别是,你想要 vector::erase()std::remove_if() .您想要的删除版本是采用一对迭代器而不是一次删除一个迭代器的版本。当你调用它时,它看起来像 m_queue_.erase(XXXX, m_queue_.end())。 .现在什么是 XXXX - 最终成为 remove_if 的返回值. remove_if 的参数是一对迭代器和一个一元谓词。 (即一个函数,它接受 const T& 作为 vector 中的任何内容,如果它应该被删除则返回 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)));

如果您的 vector 包含 nullptr 是有效的或者您不应该取消引用的其他值,将这些检查添加到谓词函数中。此外,如果 vector 拥有这些项目,您可能希望将其设为 vector<std::unique_ptr<Item>>而不是 vector<Item*> ,如果你不这样做,那么使用 erase(remove_if) 习惯用法可能会泄漏。它还使您无需记住删除内容。

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

供引用:

关于c++ - 从 vector 中删除项目时出现 Valgrind 错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40256546/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com