Alex Neumann Alex Neumann - 1 month ago 18
C++ Question

Calling virtual function - segmentation fault

The way the code works is that there is a base pure virtual class called "Module" with subclasses of "Living", "Manufacturing" and "PowerGen" which uses some of the virtual functions. In the main driver file, I am successfully able to read a list of modules from a file and store them in a vector of Module pointers. The problem comes when I want to use the virtual display function to display that vectors element's module details. I am faced with a segmentation fault. Any help would be much appreciated!

Class Module
{
public:
Module() = default;
explicit Module(const string& purpose, const string& id)
{
this->purpose = purpose;
this->id = id;
}
explicit Module(const string& purpose) { this->purpose = purpose; }
virtual ~Module() { }

virtual void setCrew( ) = 0;
virtual void display( ) const = 0;
void addCrew(const string& name)
{
crew_list.push_back(Crew(name));
++number_of_crew;
}
void setPowerReq(double power) { this->power = power; }
void setMaxCrew(int max_crew) { this->max_crew = max_crew; }

string getPurpose( ) { return purpose; }
string getId( ) { return id; }
int getNumberOfCrew( ) { return number_of_crew; }
int getMaxCrew( ) { return max_crew; }
double getPower( ) { return power; }

friend ostream& operator << ( ostream& os, const Module& m );
friend ofstream& operator << ( ofstream& os, const Module& m );
friend ifstream& operator >> ( ifstream& is, Module& m );

private:
string purpose = "unknown";
string id = "unknown";
int number_of_crew = 0;
int max_crew = 0;
double power = 0;
vector<Crew> crew_list;
};


class Living : public Module
{
public :

Living( ) = default;
explicit Living(const string& purpose);
Living(int meals, const string& id);
void setCrew( )
{
setMaxCrew( floor(meals / 3) );
setPowerReq( meals * 1.4 );
}
void display( ) const
{
cout << *this;
}

friend ofstream& operator << ( ofstream& os, const Living& l );
friend ifstream& operator >> ( ifstream& is, Living& l );

private :
int meals = 0;
static string type;
};

class Manufacturing : public Module
{
public:
Manufacturing( ) = default;
Manufacturing( const string& product, int quantity, const string& id );
explicit Manufacturing( const string& purpose );
void setCrew( )
{
setPowerReq( quantity * 6 );
setMaxCrew( maxCrew );
}
void display( ) const
{
cout << *this;
}

friend ofstream& operator << ( ofstream& os, const Manufacturing& m );
friend ifstream& operator >> ( ifstream& is, Manufacturing& m );

private:
static string type;
string product = "unknown";
static int maxCrew;
int quantity = 0;
};

class PowerGen : public Module
{
public:
PowerGen() = default;
PowerGen( int number_of_generators, const string& id );
explicit PowerGen( const string& purpose );
void setCrew( )
{
setMaxCrew(0);
setPowerReq( -(number_of_generators * 7) );
}
void display( ) const
{
cout << *this;
}

friend ofstream& operator << ( ofstream& os, const PowerGen& p );
friend ifstream& operator >> ( ifstream& is, PowerGen& p );

private:
static string type;
const int max_generators = 8;
int number_of_generators = 0;
};


Portion relevant from the main driver file which when called causes the segmenation fault:

if( cityVector.size() > 0 )
{
cout << "\nHere is the complete City" << endl;
for ( auto iter : cityVector )
{
iter->display( );
cout << endl << endl;
}
} else {
cout << "\nThere are no Modules in the City" << endl;
}


This is how I loaded the data from a file, where VMOD = vector:

void load( VMOD & cityVector )
{
string filename;
cout << "Please enter a filename >> ";
getline( cin, filename );
ifstream fin;
fin.open(filename);
if (!fin.fail())
{
while ( !fin.eof() )
{
string purpose;
getline( fin, purpose );
if ( purpose == "Living" )
{
Living object(purpose);
loadObject( cityVector, object, fin );
} else if ( purpose == "Manufacturing" )
{
Manufacturing object(purpose);
loadObject( cityVector, object, fin );
} else if ( purpose == "Power Generation" )
{
PowerGen object(purpose);
loadObject( cityVector, object, fin );
} else {

}
}
cout << "The city has " << cityVector.size() << " modules!" << endl;
} else {
cout << "File does not exist... " << endl;
}
fin.close();
}

template<typename T>
void loadObject( VMOD & cityVector, T object, ifstream& fin )
{
fin >> object;
Module* mptr;
mptr = &object;
cityVector.push_back(mptr);
}


With the file looking like this:

Living
L1
5
21
2
First Person
Fourth Person
15
Manufacturing
M1
10
12
4
Second Person
Third Person
Fifth Person
Sixth Person
C++ ness
2
Power Generation
P1
0
-14
0
2

Answer

You have a dangling pointer problem.

template<typename T>
void loadObject( VMOD & cityVector, T object, ifstream& fin )
{
   // object is a function local object
   // since it is passed by value.
   fin >> object;
   Module* mptr;

   // This is a pointer to a local object.
   // It is invalid when the function returns.
   mptr = &object;

   // Storing a pointer that will be a dangling pointer as soon
   // the function returns.
   cityVector.push_back(mptr);
}

You need to use dynamically allocated memory for the restored objects and save them in cityVector.

You'll need to make sure that memory is deallocated once you are done using it.

template<typename T>
void loadObject(VMOD& cityVector, ifstream& fin )
{
   T* objectPtr = new T;
   fin >> *objectPtr;
   cityVector.push_back(objectPtr);
}

and call it using the following syntax:

loadObject<LivingObject>(cityVector, fin);
Comments