C++ 指令指针异常行为

C++ 指令指针异常行为,c++,segmentation-fault,C++,Segmentation Fault,我遇到了一个非常奇怪的问题 假设我有一个类,它的一个公共成员是指向某种类型的指针向量: class SystemState { // ... public: SystemState (const SystemState& s); // copy constructor std::vector <Concept *> m_L; // ... }; 向量通常存储指向从基概念类继承的类的指针 在某些时候,当调用SystemStatecopy构造函数时,程序会由于分

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

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

class SystemState
{
  // ...
public:
  SystemState (const SystemState& s); // copy constructor
  std::vector <Concept *> m_L;
  // ...
};
向量通常存储指向从基
概念
类继承的类的指针

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

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)
程序不再崩溃,正在泄漏内存:正如您所看到的,我们存储了另一个对象,而没有释放旧对象的内存。 现在,如果我发布一个
delete(m_L[I])就在赋值之前,程序再次崩溃,即使指针指向有效对象(用GDB检查,但我需要更准确地检查)

更新2 我决定转到
boost::shared\u ptr
,这样就不再需要显式
删除了。

:

如果一个类定义了以下其中一项,那么它可能应该显式地定义这三项[1]:

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

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

赋值操作后,
t
s
具有相同的
m_L
向量。也就是说,它们都包含指向添加到
s
概念的指针。在声明
s
的块末尾,
s
超出范围并被销毁
~SystemState
删除
s指针,但
t
仍保留它。现在
t
m\u L
中有一个指针,指向一个被破坏的对象。只要
t
做了任何有趣的事情(包括超出范围和被销毁),它就会调用未定义的行为。

我快速浏览了一下代码。我猜是这样的:你有指向某个对象的向量m_L的指针。考虑当这个向量Myl成长时会发生什么。在内部,它分配一个新的缓冲区,将“旧东西”复制到那里,一切看起来都很好。但是现在旧的指针仍然指向释放的内存。在许多情况下,这是导致问题的常见原因,也称为“Abstraction泄漏”。是否已考虑到这一点?这也解释了为什么它似乎“工作了几次”。向量实际值在代码的特定部分增长:在另一个类的构造函数中,并且每次调用复制构造函数时(它通过
push_back
一次添加一个元素),顺便说一句,就是这个segfault的情况。据我所知,没有指针指向向量。不过,我会查一查的。真的很好的小费,谢谢!我不知道这条规则。但是,最初我选择不定义赋值运算符,因为调用
vector::erase
时出现问题(崩溃)。我现在已经定义了它:程序不再崩溃,但我正在泄漏内存,我看不到任何解决方法(我编辑了主要帖子以包含新代码)。更新:解决方法是切换到
boost::shared\u ptr
。现在代码依赖于Boost库,但至少它不再泄漏内存。
SystemState&
SystemState::operator= (const SystemState& s)
{
  for (uint32_t i = 0; i < GetSize (); i++)
  {
    m_L[i] = s[i]->Clone ();
  }

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