草庐IT

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

coder 2024-02-22 原文

对于你们中的大多数人来说,这可能看起来像是重复的。但是我花了很多时间来解决这个问题。实现了 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/

有关c++ - 从 vector 中删除项目时出现 Valgrind 错误的更多相关文章

  1. ruby - ECONNRESET (Whois::ConnectionError) - 尝试在 Ruby 中查询 Whois 时出错 - 2

    我正在用Ruby编写一个简单的程序来检查域列表是否被占用。基本上它循环遍历列表,并使用以下函数进行检查。require'rubygems'require'whois'defcheck_domain(domain)c=Whois::Client.newc.query("google.com").available?end程序不断出错(即使我在google.com中进行硬编码),并打印以下消息。鉴于该程序非常简单,我已经没有什么想法了-有什么建议吗?/Library/Ruby/Gems/1.8/gems/whois-2.0.2/lib/whois/server/adapters/base.

  2. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  3. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

    我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

  4. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  5. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

  6. ruby - 我可以使用 Ruby 从 CSV 中删除列吗? - 2

    查看Ruby的CSV库的文档,我非常确定这是可能且简单的。我只需要使用Ruby删除CSV文件的前三列,但我没有成功运行它。 最佳答案 csv_table=CSV.read(file_path_in,:headers=>true)csv_table.delete("header_name")csv_table.to_csv#=>ThenewCSVinstringformat检查CSV::Table文档:http://ruby-doc.org/stdlib-1.9.2/libdoc/csv/rdoc/CSV/Table.html

  7. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  8. ruby - 我可以使用 aws-sdk-ruby 在 AWS S3 上使用事务性文件删除/上传吗? - 2

    我发现ActiveRecord::Base.transaction在复杂方法中非常有效。我想知道是否可以在如下事务中从AWSS3上传/删除文件:S3Object.transactiondo#writeintofiles#raiseanexceptionend引发异常后,每个操作都应在S3上回滚。S3Object这可能吗?? 最佳答案 虽然S3API具有批量删除功能,但它不支持事务,因为每个删除操作都可以独立于其他操作成功/失败。该API不提供任何批量上传功能(通过PUT或POST),因此每个上传操作都是通过一个独立的API调用完成的

  9. ruby-on-rails - 项目升级后 Pow 不会更改 ruby​​ 版本 - 2

    我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby​​版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby​​版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘

  10. ruby-on-rails - 新 Rails 项目 : 'bundle install' can't install rails in gemfile - 2

    我已经像这样安装了一个新的Rails项目:$railsnewsite它执行并到达:bundleinstall但是当它似乎尝试安装依赖项时我得到了这个错误Gem::Ext::BuildError:ERROR:Failedtobuildgemnativeextension./System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/rubyextconf.rbcheckingforlibkern/OSAtomic.h...yescreatingMakefilemake"DESTDIR="cleanmake"DESTDIR="

随机推荐