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

时间:2011-11-29 07:55:24

标签: c++ iterator segmentation-fault set

我的代码的这部分(this project)给了我一个分段错误。源代码可用here

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和backtrace时出现错误。

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类的operator +。

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

我知道设计不是最好的,但我正在尝试实现该项目的清单项目,该项目涵盖了面向对象编程的各个方面。清单也可以在github上找到。

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

1 个答案:

答案 0 :(得分:5)

StackOverflow有一些“什么是分段错误?”风格Q&amp; A:

What is a segmentation fault?

理想情况下,您在具有调试器的环境中工作,并且能够逐行执行代码或放置断点。这可以帮助您隔离崩溃周围的情况。你已经在堆栈跟踪中有一个行号 - 我们假设这是一个吸烟枪:

cout<<*sit<<endl;

但是通过调试器逐步完成可以回答这样的问题,例如这是否是第一次发生在循环中......如果不是,那么在哪个元素上。


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

comparing iterators from different containers

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

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点:Operator overloading

  • 发布代码示例时,请尝试保持可读性,不需要显示很多滚动条。如果您在预览中注意到它正在放置一个较长的水平滚动条,则会中断对齐。不要为每个支撑使用单独的线,而是将它们放在与条件相同的线上。 (无论您在代码库中使用什么约定,在网上寻求技术帮助时,更简洁。)

(还提供上下文。如果你没有说它的作业和你自己的设计,那么像我这样的人会去谷歌搜索你想要使用什么类型的包管理器。幸运的是我找到了你的programmers.stackexchange.com发布...)