指令指针奇怪的行为

时间:2011-06-30 16:09:13

标签: c++ segmentation-fault

我遇到了一个非常奇怪的问题。

假设我有一个类,其中一个公共成员是指向某种类型的指针的向量:

class SystemState
{
  // ...
public:
  SystemState (const SystemState& s); // copy constructor
  std::vector <Concept *> m_L;
  // ...
};

我担心知道Concept类有一些虚拟方法很重要,其中包括:

virtual enum ConceptType GetType (void) const;

向量通常存储指向从基类Concept类继承的类的指针。

在某些时候,当调用SystemState复制构造函数时,程序会因分段错误而崩溃。 我必须说,之前已经多次调用过这样的复制构造函数,并且它没有引起任何问题。

Valgrind没有报告任何问题:   - 没有明确丢失的字节,没有间接丢失的字节(由于std :: string,只有可能丢失+仍然可达的字节)   - 没有无效读取,没有无效写入   - 当将GDB附加到Valgrind时,没有任何反应,因为Valgrind没有发现任何错误。

Intrestingl,仅通过GDB检查代码就会显示扩展指令指针,在没有合理解释的情况下变得混乱。这是一个调试会话:

SystemState (this=0xbfffecf4, s=...) at system-state.cc:14
14  SystemState::SystemState (const SystemState& s) // copy constructor
(gdb) n
16    for (uint32_t i = 0; i < s.GetSize (); i++)
(gdb) n
18      if (s.m_L[i]->GetType() == Concept::GENERIC)
(gdb) s
std::vector<sim::Concept*, std::allocator<sim::Concept*> >::operator[] (this=0x80a5d88, __n=0) at /usr/include/c++/4.3/bits/stl_vector.h:578
578       { return *(this->_M_impl._M_start + __n); }
(gdb) fin
Run till exit from #0  std::vector<sim::Concept*, std::allocator<sim::Concept*> >::operator[] (this=0x80a5d88, __n=0)
    at /usr/include/c++/4.3/bits/stl_vector.h:578
0x080782ab in SystemState (this=0xbfffecf4, s=...) at system-state.cc:18
18      if (s.m_L[i]->GetType() == Concept::GENERIC)
Value returned is $30 = (class sim::Concept * const&) @0x80a6400: 0x80a6698
(gdb) si
0x080782ad  18      if (s.m_L[i]->GetType() == Concept::GENERIC)
(gdb)
0x080782af  18      if (s.m_L[i]->GetType() == Concept::GENERIC)
(gdb)
0x080782b4  18      if (s.m_L[i]->GetType() == Concept::GENERIC)
(gdb)
0x080782b7  18      if (s.m_L[i]->GetType() == Concept::GENERIC)
(gdb)
0x080a5b10 in ?? ()
(gdb) info fr
Stack level 0, frame at 0xbfffeca0:
 eip = 0x80a5b10; saved eip 0x807bc84
 called by frame at 0xbfffed50
 Arglist at 0xbfffec98, args:
 Locals at 0xbfffec98, Previous frame's sp is 0xbfffeca0
 Saved registers:
  ebp at 0xbfffec98, eip at 0xbfffec9c
0x080a5b12 in ?? ()
(gdb)
0x080a5b14 in ?? ()
(gdb)
0x080a5b15 in ?? ()
(gdb)
0x080a5b17 in ?? ()
(gdb)

Program received signal SIGSEGV, Segmentation fault.
0x080a5b17 in ?? ()
(gdb)

作为辅助信息,Concept :: GENERIC是enum Concept::ConceptType的条目。 可以看出,访问向量没有问题()。然后,发生了一些奇怪的事情。

最令我困惑的是,之前已经多次调用过那部分代码。

我完全不知道还有什么要寻找的,也没有想法如何调试代码。 所以,要么你有这个神秘的解决方案或调试技巧,你最受欢迎!

PS:完整的文件可以在这里找到: http://mercurial.intuxication.org/hg/lte_sim/file/544cef78b03d/src

更新1

根据Rob的建议,我将operator=定义如下:

SystemState&
SystemState::operator= (const SystemState& s)
{
  for (uint32_t i = 0; i < GetSize (); i++)
  {
    m_L[i] = s[i]->Clone ();
  }

  return *this;
}

程序不再崩溃,正在泄漏内存:正如您所看到的那样,我们存储另一个对象而不释放旧内存。 现在,如果我在赋值之前发出delete (m_L[i]);,程序会再次崩溃,即使指针指向有效对象(使用GDB检查,但我需要更准确地检查它)。

更新2 我决定转移到boost::shared_ptr,以便不再需要明确的delete

1 个答案:

答案 0 :(得分:2)

Rule of Three

  

如果一个类定义了下面的一个,它应该明确定义所有三个[1]:

     
      
  •   
  • 复制构造函数
  •   
  • 复制分配操作员
  •   

您的类SystemState定义了析构函数和复制构造函数,但不定义了复制赋值运算符。考虑:

SystemState t;
{
    SystemState s;
    s.AddConcept(new ...);
    t = s;
}

在分配操作后,ts具有相同的m_L向量。也就是说,他们这两个都包含指向Concept的{​​{1}}指针。在声明s的块的末尾,s超出范围并被销毁。 s ~SystemState是指针,但delete仍然保留它。现在tt中有一个指向被破坏对象的指针。只要m_L执行任何有趣的事情(包括超出范围并被自身销毁),它就会调用未定义的行为。