I'm experiencing a very weird problem.
Let's say I have a class, a public member of which is a vector of pointers to some type:
class SystemState
{
// ...
public:
SystemState (const SystemState& s); // copy constructor
std::vector <Concept *> m_L;
// ...
};
I fear it is important to know that the Concept
class has some virtual methods, among which:
virtual enum ConceptType GetType (void) const;
The vector usually stores pointers to classes that inherit from the base Concept
class.
At some point, when calling SystemState
copy constructor, the program crashes due to segmentation fault.
I must say that such copy constructor was already been called many times before, and it didn't cause any problem.
Valgrind does not report any problem: - no definitely lost bytes, no indirectly lost bytes (only possibly lost+still reachable bytes, due to std::string) - no invalid reads, no invalid writes - when attaching GDB to Valgrind, nothing happens, as Valgrind does not spot any error.
Intrestingl, inspecting the code by means of GDB alone reveals the the extended instruction pointer, goes haywire without a reasonable explanation. Here's a debugging session:
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)
As a side information, Concept::GENERIC is an entry of enum Concept::ConceptType
.
As can be seen, the vector is accessed without problems (). Then, something strange happens.
What puzzles me most is that, that part of code has already been called several times before.
I have absolutely no idea what else to look for, nor I have ideas how to debug the code. So, either you have the solution to this mystery or debugging tips, you're most welcome!
PS: complete files can be found here: http://mercurial.intuxication.org/hg/lte_sim/file/544cef78b03d/src
Update 1
Following Rob's suggestion, I defined operator=
as follows:
SystemState&
SystemState::operator= (const SystemState& s)
{
for (uint32_t i = 0; i < GetSize (); i++)
{
m_L[i] = s[i]->Clone ();
}
return *this;
}
The program does not crash anymore, but is leaking memory: as you can see we store another object without releasing the memory of the old one.
Now, if I issue a delete (m_L[i]);
right before the assignment, the program crashes again, even if the pointer is pointing to a valid object (checked with GDB, but I need to check it more accurately).
开发者_运维百科Update 2
I decided to move to boost::shared_ptr
, so that explicit delete
is not needed anymore.
Rule of Three:
If a class defines one of the following it should probably explicitly define all three[1]:
- destructor
- copy constructor
- copy assignment operator
Your class SystemState
defines a destructor and a copy constructor, but not a copy assignment operator. Consider:
SystemState t;
{
SystemState s;
s.AddConcept(new ...);
t = s;
}
After the assignment operation, t
and s
have identical m_L
vectors. That is, they both contain pointers to a Concept
that was added to s
. At the end of the block in which s
is declared, s
goes out of scope and is destroyed. ~SystemState
delete
s the pointer, but t
still holds it. Now t
has a pointer in its m_L
that points to a destroyed object. As soon as t
does anything interesting (including going out of scope and being destroyed itself), it invokes undefined behavior.
精彩评论