【发布时间】:2011-06-30 16:09:13
【问题描述】:
我遇到了一个非常奇怪的问题。
假设我有一个类,它的公共成员是指向某种类型的指针向量:
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了。
【问题讨论】:
-
我快速浏览了代码。这就是我的猜测:您有指向某个对象的向量 m_L 的指针。考虑当这个向量 m_L 增长时会发生什么。在内部,它分配一个新的缓冲区,在那里复制“旧的东西”,一切看起来都很好。但是现在旧的指针仍然指向释放的内存。在许多情况下,这是导致问题的常见原因,也称为“抽象泄漏”。有没有考虑到这一点?这也可以解释为什么它似乎“工作了几次”。
-
向量实际在代码的特定部分增长:在另一个类的构造函数中,每次调用复制构造函数时(它通过
push_back一次添加一个元素),顺便说一下,这个段错误就是这种情况。据我所知,没有指针指向向量。不过,我会检查一下。