M
Martin Magnusson
I'm getting segmentation faults when trying to fix a memory leak in my
program. The problem is related to lists of pointers which get passed
around between objects.
Here is a description of how the code works:
I have a stack consisting of nodes and arguments, much like a function
execution stack. In this example there are only three nodes on the
stack. For each run through the loop, a copy of the current sensation is
written to the top-most node, node_C (which is a MaxLeaf). This
sensation object is then copied to the second node from the top, node_B,
and is prepended to a list which node_B keeps. After the copy, node_C's
copy of the sensation is deleted to prevent memory leaking. When node_B
terminates, it passes its sensation list up to node_A. Here, the list as
the correct number of elements, but they all seem to be invalid. When
trying to call the ToString() method on the sensations in the list, I
get a segmentation fault. If I never call delete this doesn't happen.
I'm sorry for the lengthy code, but I have tried to delete all that
isn't necessary.
The problem has to do with the functions TaxiSensation::Clone(),
MaxNode::AddSensation(), MaxNode::AddSensations(),
MaxNode::ClearSequence() and MaxNode::SensationSequence().
If someone could explain why the deletes in MaxNode::ClearSequence() are
bad, I'd be very grateful! The code should compile with no problems,
just "g++ test.cpp" works in any case.
/ martin
// Code begins here: /////////////////////////////////////////////////
#include <sstream>
#include <iostream>
#include <vector>
#include <list>
using namespace std;
//////////////////////////////////////////////////////////////////////
class Sensation {
public:
Sensation() {}
virtual ~Sensation() {}
virtual std::string ToString() const = 0;
virtual Sensation* Clone() const = 0;
};
//////////////////////////////////////////////////////////////////////
class TaxiSensation : public Sensation
{
public:
TaxiSensation()
{ m_Walls = vector<int>( 25, 0 ); }
TaxiSensation* Clone() const
{ return new TaxiSensation(*this); }
std::string TaxiSensation::ToString() const
{ return "(this is a sensation)"; }
int X;
private:
vector<int> m_Walls; // Positions of walls.
};
//////////////////////////////////////////////////////////////////////
typedef list<const Sensation*> seq_type;
//////////////////////////////////////////////////////////////////////
class MaxNode
{
public:
MaxNode( std::string name = "NN" )
{
m_Name = name;
m_SensationSequence.clear();
}
virtual ~MaxNode()
{}
void AddChild( MaxNode* p_child )
{ m_Children.push_back( p_child ); }
virtual bool IsTerminated( const float argument,
const Sensation* p_sensation ) const
{
if (((TaxiSensation*)p_sensation)->X >= 2)
{
cerr << m_Name << " terminated.\n";
return true;
}
else
return false;
}
virtual bool IsInternal() const
{ return true; }
virtual void Update( const float argument,
const seq_type sensations)
{
cerr << "Updating " << m_Name << "\n";
for (seq_type::const_iterator i = sensations.begin();
i != sensations.end();
++i)
cerr << " " << (*i)->ToString() << "\n";
}
// MaxNode::AddSensation
void AddSensation( const Sensation* const p_sensation )
{ m_SensationSequence.push_front( p_sensation->Clone() ); }
// MaxNode::AddSensations
void AddSensations( seq_type sensations )
{
m_SensationSequence.insert( m_SensationSequence.begin(),
sensations.begin(),
sensations.end() );
}
// Is there a problem here? Is it not OK to delete the list elements?
void ClearSequence()
{
for (seq_type::const_iterator i = m_SensationSequence.begin();
i != m_SensationSequence.end();
++i)
delete *i;
m_SensationSequence.clear();
}
// MaxNode::SensationSequence
const seq_type SensationSequence() const
{ return m_SensationSequence; }
vector<MaxNode*> m_Children;
protected:
std::string m_Name;
seq_type m_SensationSequence;
};
//////////////////////////////////////////////////////////////////////
class MaxLeaf : public MaxNode
{
public:
MaxLeaf( std::string name = "NN" )
{ m_Name = name; }
virtual ~MaxLeaf() {}
bool IsInternal() const
{ return false; }
bool IsTerminated( const float argument, const Sensation* p_sens) const
{ return true; }
};
/*========================================================================*/
int main()
{
typedef std::list< std:air< MaxNode*, float > > stack_type;
stack_type active_nodes;
MaxNode* node_A = new MaxNode( "Node A" );
MaxNode* node_B = new MaxNode( "Node B" );
MaxLeaf* node_C = new MaxLeaf( "Node C" );
node_A->AddChild( node_B );
node_B->AddChild( node_C );
TaxiSensation* sens = new TaxiSensation;
active_nodes.push_back( pair<MaxNode*,float>( node_A, 0 ) );
for (int n = 0; n < 5; ++n)
{
((TaxiSensation*)sens)->X = n;
cerr << "Filling stack";
while (active_nodes.back().first->IsInternal())
{
cerr << ".";
pair<MaxNode*,float> p =
pair<MaxNode*,float>( active_nodes.back().first->m_Children[0], n );
active_nodes.push_back( p );
}
(active_nodes.back().first)->AddSensation( sens );
cerr << "\nCheck for termination\n";
for (stack_type::reverse_iterator i = active_nodes.rbegin();
i != active_nodes.rend();
/*noop*/)
{
// First update the leaf node, node_C
if (!(i->first->IsInternal()))
{
i->first->Update( i->second,
i->first->SensationSequence() );
}
// Also update the parent node of any node that is terminated
// (node_C is always terminated, node_B and node_A terminates
// the third time through this loop.
if (i->first->IsTerminated( i->second, sens ))
{
if (i->first == active_nodes.front().first)
exit(0);
else
{
MaxNode* child = i->first;
++i;
i->first->Update( i->second, child->SensationSequence() );
// Prepend a Sensation to (*i)'s list.
// node_B gets one Sensation at a time here, while node_A
// should get a list of 3 elements when node_B terminates.
i->first->AddSensations( (child->SensationSequence()) );
child->ClearSequence();
active_nodes.erase( i.base(), active_nodes.end() );
}
}
else
{
++i;
}
}
}
return 0;
}
program. The problem is related to lists of pointers which get passed
around between objects.
Here is a description of how the code works:
I have a stack consisting of nodes and arguments, much like a function
execution stack. In this example there are only three nodes on the
stack. For each run through the loop, a copy of the current sensation is
written to the top-most node, node_C (which is a MaxLeaf). This
sensation object is then copied to the second node from the top, node_B,
and is prepended to a list which node_B keeps. After the copy, node_C's
copy of the sensation is deleted to prevent memory leaking. When node_B
terminates, it passes its sensation list up to node_A. Here, the list as
the correct number of elements, but they all seem to be invalid. When
trying to call the ToString() method on the sensations in the list, I
get a segmentation fault. If I never call delete this doesn't happen.
I'm sorry for the lengthy code, but I have tried to delete all that
isn't necessary.
The problem has to do with the functions TaxiSensation::Clone(),
MaxNode::AddSensation(), MaxNode::AddSensations(),
MaxNode::ClearSequence() and MaxNode::SensationSequence().
If someone could explain why the deletes in MaxNode::ClearSequence() are
bad, I'd be very grateful! The code should compile with no problems,
just "g++ test.cpp" works in any case.
/ martin
// Code begins here: /////////////////////////////////////////////////
#include <sstream>
#include <iostream>
#include <vector>
#include <list>
using namespace std;
//////////////////////////////////////////////////////////////////////
class Sensation {
public:
Sensation() {}
virtual ~Sensation() {}
virtual std::string ToString() const = 0;
virtual Sensation* Clone() const = 0;
};
//////////////////////////////////////////////////////////////////////
class TaxiSensation : public Sensation
{
public:
TaxiSensation()
{ m_Walls = vector<int>( 25, 0 ); }
TaxiSensation* Clone() const
{ return new TaxiSensation(*this); }
std::string TaxiSensation::ToString() const
{ return "(this is a sensation)"; }
int X;
private:
vector<int> m_Walls; // Positions of walls.
};
//////////////////////////////////////////////////////////////////////
typedef list<const Sensation*> seq_type;
//////////////////////////////////////////////////////////////////////
class MaxNode
{
public:
MaxNode( std::string name = "NN" )
{
m_Name = name;
m_SensationSequence.clear();
}
virtual ~MaxNode()
{}
void AddChild( MaxNode* p_child )
{ m_Children.push_back( p_child ); }
virtual bool IsTerminated( const float argument,
const Sensation* p_sensation ) const
{
if (((TaxiSensation*)p_sensation)->X >= 2)
{
cerr << m_Name << " terminated.\n";
return true;
}
else
return false;
}
virtual bool IsInternal() const
{ return true; }
virtual void Update( const float argument,
const seq_type sensations)
{
cerr << "Updating " << m_Name << "\n";
for (seq_type::const_iterator i = sensations.begin();
i != sensations.end();
++i)
cerr << " " << (*i)->ToString() << "\n";
}
// MaxNode::AddSensation
void AddSensation( const Sensation* const p_sensation )
{ m_SensationSequence.push_front( p_sensation->Clone() ); }
// MaxNode::AddSensations
void AddSensations( seq_type sensations )
{
m_SensationSequence.insert( m_SensationSequence.begin(),
sensations.begin(),
sensations.end() );
}
// Is there a problem here? Is it not OK to delete the list elements?
void ClearSequence()
{
for (seq_type::const_iterator i = m_SensationSequence.begin();
i != m_SensationSequence.end();
++i)
delete *i;
m_SensationSequence.clear();
}
// MaxNode::SensationSequence
const seq_type SensationSequence() const
{ return m_SensationSequence; }
vector<MaxNode*> m_Children;
protected:
std::string m_Name;
seq_type m_SensationSequence;
};
//////////////////////////////////////////////////////////////////////
class MaxLeaf : public MaxNode
{
public:
MaxLeaf( std::string name = "NN" )
{ m_Name = name; }
virtual ~MaxLeaf() {}
bool IsInternal() const
{ return false; }
bool IsTerminated( const float argument, const Sensation* p_sens) const
{ return true; }
};
/*========================================================================*/
int main()
{
typedef std::list< std:air< MaxNode*, float > > stack_type;
stack_type active_nodes;
MaxNode* node_A = new MaxNode( "Node A" );
MaxNode* node_B = new MaxNode( "Node B" );
MaxLeaf* node_C = new MaxLeaf( "Node C" );
node_A->AddChild( node_B );
node_B->AddChild( node_C );
TaxiSensation* sens = new TaxiSensation;
active_nodes.push_back( pair<MaxNode*,float>( node_A, 0 ) );
for (int n = 0; n < 5; ++n)
{
((TaxiSensation*)sens)->X = n;
cerr << "Filling stack";
while (active_nodes.back().first->IsInternal())
{
cerr << ".";
pair<MaxNode*,float> p =
pair<MaxNode*,float>( active_nodes.back().first->m_Children[0], n );
active_nodes.push_back( p );
}
(active_nodes.back().first)->AddSensation( sens );
cerr << "\nCheck for termination\n";
for (stack_type::reverse_iterator i = active_nodes.rbegin();
i != active_nodes.rend();
/*noop*/)
{
// First update the leaf node, node_C
if (!(i->first->IsInternal()))
{
i->first->Update( i->second,
i->first->SensationSequence() );
}
// Also update the parent node of any node that is terminated
// (node_C is always terminated, node_B and node_A terminates
// the third time through this loop.
if (i->first->IsTerminated( i->second, sens ))
{
if (i->first == active_nodes.front().first)
exit(0);
else
{
MaxNode* child = i->first;
++i;
i->first->Update( i->second, child->SensationSequence() );
// Prepend a Sensation to (*i)'s list.
// node_B gets one Sensation at a time here, while node_A
// should get a list of 3 elements when node_B terminates.
i->first->AddSensations( (child->SensationSequence()) );
child->ClearSequence();
active_nodes.erase( i.base(), active_nodes.end() );
}
}
else
{
++i;
}
}
}
return 0;
}