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
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);
Related
I have parent class:
class Data
{
public:
Data ( void ) { }
Virtual int Size ( void )
{
return 100;
}
protected:
map<string, Data*> m;
};
Classes that inherit from class Data:
class Struct : public Data
{
public:
Struct ( void ) { }
Struct & Add ( const string & name, Data x )
{
Data * tmp = new Data ( x );
m[name] = tmp;
return *this;
}
void Print ( void )
{
for ( const auto & tmp : m )
cout << tmp . first << " " << tmp . second -> Size () << endl;
}
};
class IntData : public Data
{
public:
IntData ( void ) { }
int Size ( void )
{
return 4;
}
};
class DoubleData : public Data
{
public:
DoubleData ( void ) { }
int Size ( void )
{
return 8;
}
};
main :
int main ( void )
{
Struct a;
a . Add ( "Integer",IntData () );
a . Print ();
return 0;
}
Current output : Integer 100
Expected output : Integer 4
I want to create a map which would hold various types of objects that are derived from Data class. But when i want to call method Size from stored object in map ( in this case IntData ) which should return 4 It always returns value from parent class Data. How could i fix that please?
There's your problem:
Data * tmp = new Data ( x );
The actual pointer you're putting into the map is an instance of the Data parent class. You're copy-constructing a new instance of the Data parent class from an argument that you're passing, by value, as a parameter.
You need to change this whole function to:
Struct & Add ( const string & name, Data *x)
{
m[name] = x;
return *this;
}
And the caller is now responsible for constructing a new instance of any subclass:
a . Add ( "Integer",new IntData);
Then, this will work as you intended.
Of course, this kind of an approach brings up various issues with memory leaks, etc..., so you're better off using std::shared_ptr. But that would be a different question...
I've rewritten your code for you.
#include <unordered_map>
#include <memory>
#include <iostream>
class Data {
public:
virtual ~Data(){}
virtual int Size() = 0;
};
class Struct : public Data {
std::unordered_map<std::string, std::unique_ptr<Data>> m;
public:
Struct& Add(const std::string& name, std::unique_ptr<Data> x) {
m[name] = std::move(x);
return *this;
}
void Print() {
for(const auto& tmp : m )
std::cout << tmp.first << " " << tmp.second->Size() << "\n";
}
int Size() override {
int sum = 0;
for (const auto& tmp : m)
sum += tmp.second->Size();
return sum;
}
};
class IntData : public Data {
public:
int Size( ) override { return 4; }
};
class DoubleData : public Data {
public:
DoubleData( ) { }
int Size( ) override { return 8; }
};
int main() {
Struct a;
a.Add("Integer", std::make_unique<IntData>() );
a.Print();
}
Your welcome.
i have the following code.
The for-loop at the end should go through the object of CCarList class, print out the the a_rz and vin of Car structure and stop when the AtEnd() method returns true.
But it doesnt stop and moreover when i try to reach the values of a_rz and vin it gives segmentation fault.
Could someone, please, explain how to use properly iterator in my CCarList class?
Thanks
typedef struct Car {
string a_rz;
unsigned int vin;
}Car;
class CCarList
{
public:
string RZ ( void ) const;
unsigned int VIN ( void ) const;
bool AtEnd ( void ) const;
void Next ( void );
vector<Car*> vCar;
vector<Car*>::const_iterator it = vCar.begin();
public:
CCarList ( void ){}
~CCarList ( void ){}
};
string CCarList::RZ ( void ) const {
return "ahoj"; //(**it).a_rz;
}
unsigned int CCarList::VIN ( void ) const{
return 5; //(**it).vin;
}
bool CCarList::AtEnd ( void ) const {
if(it == vCar.end()) return true;
return false;
}
void CCarList::Next ( void ){
it++;
}
int main() {
Car *a, *b, *c;
a = new Car;
b = new Car;
c = new Car;
(*a).a_rz = "abc";
(*a).vin = 45;
(*b).a_rz = "dfg";
(*b).vin = 65;
(*c).a_rz = "jkl";
(*c).vin = 23;
CCarList list_of_cars;
list_of_cars.vCar.push_back(a);
list_of_cars.vCar.push_back(b);
list_of_cars.vCar.push_back(c);
for ( ; ! list_of_cars . AtEnd (); list_of_cars . Next () )
cout << list_of_cars . RZ () << ", " << list_of_cars . VIN () << endl;
return 0;
}
Your problem is that iterator it is not being updated/invalidated after each push_back. After last insertion it still points to "nothing" as it was from beginning.
Soultion is simple -- update your iterator. Add a method for adding new elements:
void CCarList::Add(Car* car)
{
vCar.push_back(car);
it = vCar.begin();
}
and then just:
list_of_cars.Add(a);
list_of_cars.Add(b);
list_of_cars.Add(c);
Also regarding above problem, you're trying to wrap vector and provide same functionality that vectoralready provides. Consider moving functionality related to Car structure inside that structure. And leaving in CCarList only methods that are related to CCarList. Just a short piece of code to show you what I mean:
typedef struct Car {
string a_rz;
unsigned int vin;
} Car;
class CCarList {
public:
vector<Car*> vCar;
CCarList(void){}
~CCarList(void){}
};
int main() {
Car *a, *b, *c;
a = new Car;
b = new Car;
c = new Car;
a->a_rz = "abc";
a->vin = 45;
b->a_rz = "dfg";
a->vin = 65;
c->a_rz = "jkl";
c->vin = 23;
CCarList list_of_cars;
list_of_cars.vCar.push_back(a);
list_of_cars.vCar.push_back(b);
list_of_cars.vCar.push_back(c);
for(auto car : list_of_cars.vCar)
cout << car->a_rz << ", " << car->vin << endl;
return 0;
}
I want to write a print function for a class AutoData that has information about cars in it. With this print function, I would ideally like to print out a vector that contains many different class objects. I have already written get functions for each element of the objects, but I am still a bit unsure of how to go about using those to write a function to print out the data in the following format:
mpg:cylinders:displacement:horsepower:weight:acceleration:modelYear:origin:carName
For example:
10.0:8:360.0:215.0:4615.:14.0:70:1:ford f250
10.0:8:307.0:200.0:4376.:15.0:70:1:chevy c20
11.0:8:318.0:210.0:4382.:13.5:70:1:dodge d200
The class is:
#include <string>
#include <vector>
#include <iostream>
using namespace std;
class AutoData {
public:
AutoData()
{
mpg = 0;
cylinders = 0;
displacement = 0;
horsepower = 0;
weight = 0;
acceleration = 0;
modelYear = 0;
origin = 0;
carName = "";
}
AutoData( const AutoData & rhs)
{
setAuto(rhs.mpg, rhs.cylinders, rhs.displacement, rhs.horsepower, rhs.weight, rhs.acceleration, rhs.modelYear, rhs.origin, rhs.carName);
}
void setAuto(float mp, int cy, float di, float ho, float we, float ac, int mo, int o, string ca)
{
mpg = mp;
cylinders = cy;
displacement = di;
horsepower = ho;
weight = we;
acceleration = ac;
modelYear = mo;
origin = o;
carName = ca;
}
const float & getmpg( ) const
{
return mpg;
}
const int & getcylinders( ) const
{
return cylinders;
}
const float & getdisplacement( ) const
{
return displacement;
}
const float & gethorsepower( ) const
{
return horsepower;
}
const float & getweight( ) const
{
return weight;
}
const float & getacceleration( ) const
{
return acceleration;
}
const int & getmodelYear( ) const
{
return modelYear;
}
const int & getorigin( ) const
{
return origin;
}
const string & getcarName( ) const
{
return carName;
}
bool operator == (const AutoData & rhs ) const
{
if( getmpg( ) == rhs.getmpg( ) )
{
return gethorsepower( ) == rhs.gethorsepower( );
}
else
{
return false;
}
}
bool operator > ( const AutoData & rhs ) const
{
if( rhs.getmpg( ) > getmpg( ) )
{
return true;
}
else if( getmpg( ) == rhs.getmpg( ) )
{
if( rhs.gethorsepower( ) > gethorsepower( ) )
{
return true;
}
}
else
{
return false;
}
}
private:
float mpg;
int cylinders;
float displacement;
float horsepower;
float weight;
float acceleration;
int modelYear;
int origin;
string carName;
};
Any help/advice anyone can provide would be very much appreciated!! Thanks
If you want to be able to do std::cout << AutoData();, you need to overload the output stream operator operator<<:
std::ostream& operator<< (std::ostream &out, AutoData const& data) {
out << data.getmpg() << ':';
out << data.getcylinders() << ':';
// and so on...
return out;
}
This function is not a member function, and since you have getters for each attribute, you do not have to declare this function as friend of your class.
Then you can do:
AutoData myAuto;
std::cout << myAuto << '\n';
What have you tried so far? My approach would be overloading operator<<, like:
std::ostream& operator<<(std::ostream& out, const AutoData& dasAuto) {
return out << dasAuto.getmpg() << ':' << dasAuto.getcylinders() <<
/* insert everthing in the desired order here */
std::endl;
}
And the same thing for the "reading" function, like:
std::istream& operator>>(std::istream& in, AutoData& dasAuto) {
float mpg;
int cylinders;
float displacement;
float horsepower;
float weight;
float acceleration;
int modelYear;
int origin;
string carName;
char separator;
const char SEP = ':';
if( !(in >> mpg >> separator) || (separator != SEP) ) return in;
if( !(in >> cylinders >> separator) || (separator != SEP) ) return in;
/* rinse, repeat */
if( !std::getline(in, carName) ) return in;
dasAuto.setAuto(mpg, cylinders /*, etc etc */);
return in;
}
You can read this artical to know about friend and operator <<,
http://www.cprogramming.com/tutorial/friends.html
In the class AutoData, you should declare this function:
friend ostream& operator<< (ostream& out, const AutoData& obj);
outside the class, you should define this function like this:
ostream& operator<< (ostream& out, const AutoData& obj)
{
out<<obj.mpg<<":";
//do what you want
return out;
}
i have to serialize a CList of base and derived classes. How can i do that.
typedef CList<Student*, Student*> StVec;
class Student : public CObject
{
public:
DECLARE_SERIAL(Student);
Student();
Student(Student& s);
Student(CString _name, CString _last, int _age);
virtual ~Student(void);
virtual void cin();
virtual void cout();
virtual void Serialize(CArchive& ar);
static Student* FromInput();
inline const CString GetName(){return name;}
inline const CString GetLastName(){return last_name;}
inline const int GetAge(){return age;}
virtual inline Student& operator=( const Student &s )
{
name = s.name;
last_name = s.last_name;
age = s.age;
return *this;
}
protected:
CString name;
CString last_name;
int age;
};
class Warden : public Student
{
protected:
virtual void Serialize(CArchive& ar);
UINT salary;
};
class Group : public CObject
{
public:
DECLARE_SERIAL(Group);
enum FindType { First = 0, Last, All};
typedef bool (*StudentsFindCallback) (Student* student, void* condition);
Group(){ name = _T("Default Group"); }
Group(CString _name);
Group(Group& s);
~Group();
void CreateUser();
void ShowUsers();
//StVecOfIt FindUser(StudentsFindCallback f, void* condition, FindType ft);
inline Student* getStudent(POSITION pos) {return students->GetNext(pos); }
inline void DeleteUser(POSITION it){ students->RemoveAt(it); }
virtual void Serialize(CArchive& ar);
void Clean();
/*static bool FindbyName(Student* student, void* condition);
static bool FindbyLastName(Student* student, void* condition);
static bool FindbyAge(Student* student, void* condition);*/
virtual inline Group& operator=( const Group &s )
{
name = s.name;
students = s.students;
return *this;
}
protected:
CString name;
StVec* students;
};
void Student::Serialize(CArchive& ar)
{
CObject::Serialize( ar );
if (ar.IsStoring())
{
ar.SerializeClass(Student::GetRuntimeClass());
ar << name << last_name << age;
}
else
{
ar.SerializeClass(Student::GetRuntimeClass());
ar >> name >> last_name >> age;
}
}
void Group::Serialize(CArchive& ar)
{
ar.SerializeClass(RUNTIME_CLASS(Group));
if (ar.IsStoring())
{
ar << (int)students->GetCount();
POSITION pos = students->GetHeadPosition();
while(pos != NULL)
{
Student* student = students->GetNext(pos);
student->Serialize(ar);
}
}
else
{
Clean();
int count = 0;
ar >> count;
for(INT_PTR i = 0; i < count; ++i)
{
Student* st = new Student();
st->Serialize(ar);
students->AddTail(st);
}
}
}
I found an article at the codeproject but i'cant understend it at all.
Ofcourse i can add a flag which indicate the type of class. Also i can serialize to derived class and if the derived properties are empty use static_cast to derive it to base class. Or i'am thinking of using CArchive::ReadObject or CArchive::ReadClass to load classes, but is there another way to do it?
My MFC is rusty but I believe this should work (not tested!).
Also, your Serialize() method should never call SerializeClass for its own class. It's up to the caller to decide to call it. For instance, the << and >> operators do it to identify the class of the object that comes next in the archive.
void Student::Serialize( CArchive& ar )
{
CObject::Serialize( ar );
if (ar.IsStoring())
{ // No need for SerializeClass here.
ar << name << last_name << age;
}
else
{ // No need for SerializeClass here.
ar >> name >> last_name >> age;
}
}
void Group::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
ar << (int)students->GetCount();
POSITION pos = students->GetHeadPosition();
while(pos != NULL)
{
Student* student = students->GetNext(pos);
ar << student; // << includes class info about the serialized object
}
}
else
{
Clean();
int count = 0;
ar >> count;
for(INT_PTR i = 0; i < count; ++i)
{
CObject *p;
ar >> p; // Deserialize whatever kind of object comes next
students->AddTail(p);
}
}
}
I am trying to make a basic text editor and I'm having trouble loading the data in the struct using push_back.
Here is the struct
struct LineInfo
{
int lineNumber;
string text;
};
Here is the main program
vector<LineInfo> globalDocument;
int main()
{
cout << "COSC 120 line editing system now running." << endl << endl;
bool done = false;
while ( !done )
{
cout << "> ";
string inputBuffer;
getline( cin, inputBuffer );
int lineNumber;
splitLineNumberAndText( inputBuffer, lineNumber );
ptr.push_back( &lineNumber );
if ( lineNumber > 0 )
handleTextLine( inputBuffer, lineNumber );
else
done = handleSystemCommand( inputBuffer );
}
cout << endl << "COSC 120 line editing system has shut down. Bye." << endl;
return 0;
}
This receives a line number and text from input then splits it up into an int and a string and sends it to handleTextLine
Here is the function
void handleTextLine( const string& s, int lineNumber2 )
{
globalDocument.lineNumber.push_back(lineNumber2);
globalDocument.text.push_back(s);
}
vector<LineInfo> globalDocument is a vector of LineInfo structs, so you must push_back LineInfo objects into it:
void handleTextLine( const string& s, int lineNumber2) {
LineInfo li;
li.lineNumber = lineNumber2;
li.text = s;
globalDocument.push_back(li);
}
Or, using struct initialization syntax:
void handleTextLine( const string& s, int lineNumber2) {
LineInfo li = {lineNumber2, s};
globalDocument.push_back(li);
}
You can get even more compact insertion code if you declare a constructor for LineInfo:
struct LineInfo {
// Default constructor
LineInfo() : lineNumber(0) {}
// Constructor taking 2 arguments
LineInfo(int n, string s) : lineNumber(n), text(s) {}
int lineNumber;
string text;
};
void handleTextLine( const string& s, int lineNumber2) {
globalDocument.push_back(LineInfo(lineNumber2, s));
}