在C++中迭代 std::set<std::string> 时出现分段错误

Segmentation fault while iterating a std::set<std::string> in C++

本文关键字:std 错误 分段 gt lt C++ 迭代 set string      更新时间:2023-10-16

我的这部分代码(对于这个项目)给了我一个分段错误。源代码在这里。

void PackageManager::install_package(string pname)
{
  if(repository->exists_package(pname)) {
    Package *pkg;
    ConcretePackage *cpkg;
    MetaPackage *mpkg;
    if(repository->is_virtual(pname)) {
      //code for dealing with meta packages
      mpkg = new MetaPackage(pname);
      pkg = mpkg;
      system->operator+(pname);
    } else {
      //code for dealing with concrete packages
      cpkg = new ConcretePackage(pname);
      pkg = cpkg;
      system->operator+(pname);
      if( cpkg->getDependencies().size() > 0) {
        for(set<string>::iterator sit = pkg->getDependencies().begin();
            sit!=pkg->getDependencies().end(); ++sit) {
          cout<<*sit<<endl;
          system->operator+(*sit);
        }
      }
    }
  } else {
    cout<<"Invalid Package Name"<<endl;
  }
}

这是我运行gdb和回溯时的错误。

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7b6db03 in std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char>, std::allocator<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) ()
   from /usr/lib/libstdc++.so.6
(gdb) backtrace
#0  0x00007ffff7b6db03 in std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char>, std::allocator<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) ()
   from /usr/lib/libstdc++.so.6
#1  0x00000000004052e8 in PackageManager::install_package (this=0x7fffffffe280, pname=...) at packagemanager.cpp:39
#2  0x000000000040575a in main () at packagemanager.cpp:79

我正在尝试遍历一个集合并执行一些操作。如果需要,我可以编写更多代码。我也希望有人能引导我去一个地方,在那里我可以学习理解这些片段错误。我对它们了解不多,当我遇到它们时,我倾向于恐慌。

这是System类的操作符+。

void System::operator+(string pname)
{
  installed_packages.insert(pname);
  log.push_back("Added " + pname);
}

我知道这个设计不是最好的,但我正在努力实现这个项目清单上的项目,它涵盖了面向对象编程的各个领域。这个清单也可以在github上找到。

我已经尝试通过调试器运行代码,打印出*sit。它可以工作一段时间,然后崩溃。我对gdb了解不多

StackOverflow有几个"什么是分段错误?"风格的问题& a:

什么是分段错误?

理想情况下,您在一个有调试器的环境中工作,并且能够逐行检查代码,或者设置断点。这可以帮助您隔离您的事故周围的环境。在堆栈跟踪中已经有了行号——我们假设它指向一个确凿的证据:

cout<<*sit<<endl;

但是使用调试器可以回答诸如是否在第一次通过循环时发生这种情况之类的问题…


UPDATE:看看你在GitHub上的这段代码(不包括上面的代码),我看到ConcretePackage::getDependencies()通过值返回一个集合,而不是通过引用返回。这意味着每次调用该成员时,都会得到该集合的一个新副本。来自不同容器的迭代器不应该相互比较,即使它们是相同的类型:

比较不同容器的迭代器

要解决这个问题,您可以更改:

for(set<string>::iterator sit = pkg->getDependencies().begin();
        sit!=pkg->getDependencies().end(); ++sit) { ... }

…到:

set<string> deps = pkg->getDependencies();
for(set<string>::iterator sit = deps.begin(); sit!=deps.end(); ++sit) { ... }

…或者你可以改变getDependencies的定义,让它返回一个引用:

set<string>& ConcretePackage::getDependencies() {
    return dependencies;
}

学习一种方法与另一种方法的原因留给学生作为练习。: P


更多说明:

  • 对于要使用迭代器的集合类,不需要针对零大小的特殊情况测试。如果一个集合不包含任何元素,则该集合的.begin()将返回一个等于.end()的迭代器。上面的循环可以很好地处理这种情况,并会立即退出。

  • 在代码中显式调用operator+而不对返回值做任何操作表明您可能有某种副作用。很少有人期望像a = b + c这样的表达会改变bc……像b->operator+(c);这样的一行代码表明你正在做这样的事情。虽然技术上可行,但我会避免。参见第2点:操作符重载

  • 当你发布代码样本时,尽量保持它们的可读性,而不需要很多滚动条来显示。如果你在预览中注意到它放了一个很长的水平滚动条,就把线条分开。不要为每个大括号使用单独的行,而是将它们放在与条件相同的行上。(不管你在代码库中使用什么约定,在在线请求技术帮助时越简短越好。)

(也提供上下文。如果您不说这是您自己的作业和设计,那么像我这样的人就会去google搜索,试图弄清楚您使用的是哪种包管理器。幸运的是,我找到了你的programmers.stackexchange.com post…)