usage of istream in class constructor c++ - c++

I have a text file that contains a list of students and their marks and looks like this:
Name_of_student 78 4; 98 5; 90 5; 63 3;
...
I have an assignment to create a class that will read and store that data. This is what I've done so far.
group.h
class Subject {
public:
Subject(int mark0, int mark1);
Subject();
int get_m0() { return mark0; }
int get_m1() { return mark1; }
private:
int mark0;
int mark1;
};
class Student {
public:
Student(string name);
Student();
vector<Subject>my_marks;
string get_name() { return name; }
private:
string name;
};
class Reading
{
public:
Reading(vector<Student>, istream& );
istream& read_student();
private:
vector<Student>group;
istream& is;
};
text.cpp
Subject::Subject(int m0, int m1) :
mark0(m0), mark1(m1) {}
Subject::Subject() :
mark0(1), mark1(1) {}
Student::Student(string n0) :
name(n0) {}
Student::Student() :
name("null") {}
Reading::Reading(vector<Student>group0, istream& is0) :
group(group0), is(is0) {}
istream& Reading::read_student()
{
string n;
is >> n;
if (!is) return is;
Student st = Student(n);
for (int i = 0; i < 4; ++i)
{
int m0, m1;
is >> m0 >> m1;
char ch;
is >> ch;
Subject sub = Subject(m0, m1);
st.my_marks.push_back(sub);
}
group.push_back(st);
return is;
}
It compiles, but refuses to read anything.
int main()
{
ifstream ifs("text");
if(!ifs) error("can`t open input file");
vector<Student> group;
Readding r(group, ifs);
r.read_student();
cout << group.size();
}
And what it shows:
0
If anyone has any ideas I'd appreciate it.

In Reading::read_student(), you are filling up the member variable Reading::group. You are not filling up the function variable group in main.
Use:
int main()
{
ifstream ifs("text");
vector<Student> group;
Readding r(group, ifs);
r.read_student();
cout << r.group.size();
// ^^^^^^^^ access the member variable of r.
}
If you want the function variable group to be filled up, Reading needs to store a reference to the input group.
class Reading
{
public:
Reading(vector<Student> & , istream& );
// ^^^^^^ Take a reference as input
istream& read_student();
private:
vector<Student>& group;
// ^^^^^^ store a reference
istream& is;
};

What i have changed:
group.h
class Reading
{
public:
Reading( istream& );
vector<Student>group;
istream& read_student();
private:
istream& is;
};
text.cpp
Reading::Reading(istream& is0) :
is(is0) {}
int main()
{
ifstream ifs("text");
if(!ifs) error("can`t open input file");
Readding r(ifs);
r.read_student();
cout << r.group.size();
}
And now it works all correct!!!

Related

After writing binary file and reading a record (an object) from the binary file, enum type last attribute of the record is NULL ( default value)

I'm trying to write a C++ code that read from a text file, write all the contents into a binary file, and read a record from the binary file.To hold records, I created a Student class that one of its attribute is enum type.I read from text file successfully, I write the Student records successfully (I check it while debugging), but I have a problem with reading one Nth record from the binary file.Although all attribute are taken successfully, the last attribute whic is enum type is not changed and seems as default even for all records.Although no error exists in the VS 2017, with debugging I recognize that the problem is at binaryRead and readRecord parts (The data in "this" shows the enum attribute as NONE.Also sizeOfOneStudentRecord in seekg, read, readRecord, binaryRead etc. can be not true. I need help friends.
The code is given below. I will aprreciate your help.
enum Category { NONE = 0, Honor=1, High_Honor = 2 };
class Student
{
private:
string name;
double weight;
int age;
Category degree;
public:
Student();
Student(string _name, double _weight, int _age, Category _degree);
Student* txtRead(ifstream& myFile);
size_t sizeOfOneStudentRecord(Student* a);
void binaryWrite(fstream& myFile);
void binaryRead(fstream& myFile);
void readRecord(fstream& binmyFile, int order);
friend ostream& operator<<(ostream& output, Student& s) {
..
}
};
Student::Student()
{
name = "";
weight = 0.0;
age = 0;
degree = NONE;
}
Student::Student(string _name, double _weight, int _age, Category _degree)
{
name = _name;
weight = _weight;
age = _age;
degree = _degree;
}
size_t Student::sizeOfOneStudentRecord(Student* c) {
return sizeof(this->name) + sizeof(this->weight) + sizeof(this->age) + sizeof(this->degree);
}
Student* Student::txtRead(ifstream& myFile) {
...
}
void Student::binaryWrite(fstream& myFile) {
myFile.write((char *)this, sizeOfOneStudentRecord(this));
}
void Student::binaryRead(fstream& myFile) {
myFile.read((char*)this, sizeOfOneStudentRecord(this));
}
void Student::readRecord(fstream& binmyFile, int order) {
binmyFile.seekg((order)*sizeOfOneStudentRecord(this));
binaryRead(binmyFile);
name = this->name;
age = this->age;
weight = this->weight;
degree = this->degree;
}
int main() {
Student *StudentArr[20];
ifstream myFile("Student.txt", ios::in);
ofstream txtmyFile("newStudentFile.txt", ios::out | ios::trunc);
fstream binmyFile("StudentBin.bin", ios::out | ios::in | ios::trunc | ios::binary);
for (int i = 0; i < 20; i++) {
StudentArr[i] = new Student();
StudentArr[i]->txtRead(myFile);
txtmyFile << *StudentArr[i];
StudentArr[i]->binaryWrite(binmyFile);
}
myFile.close();
txtmyFile.close();
Student s1;
s1.readRecord(binmyFile, 8);
std::cout << s1 << endl;
binmyFile.close();
return 0;}

create objects based on entries from a file

How to create objects during runtime based on input from file.
I have few entries in a file in an organized way separated by a space
entryname type satus
entry1 type1 yes
entry2 type2 no
...
I would like to create objects of below class based on number of entries.
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
class Entry{
std::string name;
std::string type;
std::string availability;
};
void getEntriesInfo(std::vector<std::string> &info)
{
std::string line;
std::fstream myfile("entries.txt",std::ios::in);
if (myfile.is_open())
{
//remove first line from file.
getline(myfile,line);
while ( getline(myfile,line) )
info.push_back(line);
myfile.close();
}
else std::cout << "Unable to open file";
}
int main()
{
std::vector<std::string> entries_info;
getEntriesInfo(entries_info);
for(auto &e: entries_info)
std::cout<<e<<std::endl;
return 0;
}
EDIT 1:
I have restructured code a bit using maps and vectors, can it be made more elegant.
If you look at the code , I have not used constructors & destructors properly, (I could not figure out a good way to use), also the setters and not written all kinds of constructors(deep copy or move) etc..
#include <iostream>
#include <fstream>
#include <vector>
#include <sstream>
//#include <istream>
#include <map>
class AItem;
AItem makeItem(std::string );
void displayItemInfo(std::vector<std::string> info);
class Item {
public:
virtual ~Item() = default;
virtual std::string name() const = 0 ;
virtual std::string quota() const = 0 ;
virtual std::string make() const = 0 ;
};
class AItem : Item{
private:
std:: string _name;
std:: string _quota;
std:: string _make;
public:
virtual std::string name() const { return _name;}
virtual std::string quota() const { return _quota;}
virtual std::string make() const {return _make;}
void setName();
void setQuota();
void setMake();
AItem(){}
AItem(std::string name,std::string quota,std::string make) :_name(name),_quota(quota),_make(make) {}
friend std::istream& operator>>(std::istream& in,AItem& item);
friend std::ostream & operator << (std::ostream &out, const AItem & item);
};
void displayItemInfo(std::vector<std::string> info)
{
for(auto &i_it:info)
std::cout<<i_it<<std::endl;
}
std::vector<std::string> readItemInfo(const char *str)
{
std::string line;
std::vector<std::string> itemInfo;
std::ifstream file(str, std::ios::in);
if(file.is_open()){
getline(file,line);
while(getline(file,line))
itemInfo.push_back(line);
file.close();
}else{
std::cout<<"Unable to open"<<str<<std::endl;
}
return itemInfo;
}
std::ostream &operator << (std::ostream &out,const AItem& item)
{
//std::cout <<item.name()<<" "<<item.quota()<<" "<<item.make() <<std::endl;
return out <<item.name()<<" "<<item.quota()<<" "<<item.make() <<std::endl;
}
std::istream& operator >> (std::istream& in,AItem& item) {
//in >> item._name >> item._quota >> item._make;
return in >> item._name >> item._quota >> item._make;
}
AItem makeItem(std::string aitem)
{
std::stringstream ss(aitem);
std::vector<std::string> arguments;
std::string entry;
while (ss >> entry) {
arguments.push_back(entry);
}
AItem aItem_obj(arguments.at(0),arguments.at(1),arguments.at(2));
return aItem_obj;
}
std::map<std::string,AItem> createItems(std::vector<std::string> item)
{
std::map<std::string,AItem> ItemMap;
for(auto &i_it:item) {
AItem a_item = makeItem(i_it);
ItemMap.insert(std::make_pair(a_item.name(),a_item));
}
// for(auto &m:ItemMap)
// std::cout<<m.first<<m.second<<std::endl;
return ItemMap;
}
int main(int argc, char*argv[])
{
char *str = "player_info.txt";
std::vector<std::string> info;
std::map<std::string,AItem> ItemMap;
// info = readItemInfo(str);
// displayItemInfo(info);
//#if 0
ItemMap = createItems(readItemInfo(str));
for(auto &item:ItemMap)
std::cout<<item.first<<item.second<<std::endl;
//#endif
return 0;
}
EDIT 2
I have done this way
entry.h
#include <iostream>
#include <string>
class Entry{
private:
std::string name;
std::string type;
std::string availability;
public:
std::string getName(void) const;
std::string getType(void) const;
std::string getAvailability(void) const;
void setName(const std::string n);
void setType(const std::string t);
void setAvailability(const std::string y);
Entry();
~Entry();
friend std::ostream &operator << (std::ostream &out,const Entry &e);
};
entry.cpp
#include "entry.h"
std::string Entry ::getName(void) const{
return name;
}
std::string Entry ::getType(void) const{
return type;
}
std::string Entry ::getAvailability(void) const{
return availability;
}
void Entry ::setName(const std::string n){
name = n;
}
void Entry ::setType(const std::string t){
type = t;
}
void Entry ::setAvailability(const std::string y){
availability = y;
}
Entry::Entry(){
}
Entry::~Entry(){
}
std::ostream &operator << (std::ostream &out,const Entry &e){
return out << e.getName()<<" " <<e.getType()<<" "<<e.getAvailability()<<std::endl;
}
main.cpp
#include <fstream>
#include <vector>
#include "entry.h"
void makeEntry(std::string line,std::vector<Entry> &entries)
{
Entry P1;
P1.setName(line.substr(0, line.find(' ')));
P1.setType(line.substr(P1.getName().size(), line.find(' ')));
P1.setAvailability(line.substr(P1.getName().size()+P1.getType().size(), line.find(' ')));
//std::cout<<"C:"<<P1.getName()<<P1.getType()<<P1.getAvailability()<<std::endl;
entries.push_back(P1);
}
void getEntriesInfo(std::vector<std::string> &info)
{
std::string line;
std::fstream myfile("entries.txt",std::ios::in);
if (myfile.is_open())
{
//remove first line from file.
getline(myfile,line);
while ( getline(myfile,line) )
info.push_back(line);
myfile.close();
}
else std::cout << "Unable to open file";
}
int main()
{
std::vector<std::string> entries_info;
std::vector<Entry> entries;
getEntriesInfo(entries_info);
for(auto &e: entries_info) {
std::cout<<e<<std::endl;
makeEntry(e,entries);
}
std::cout<<"OUT"<<std::endl;
for(auto &e: entries) {
std::cout<<e<<std::endl;
}
return 0;
}
You can provide an overload for the stream extraction operator:
std::istream& operator>>(std::istream& in,Entry& e) {
in >> e.name >> e.type >> e.availability;
}
This works, when the individual entries in the file do not contain spaces. Ie it would fail for
entryName LastName type1 yes
In this case you would need to have a seperator (eg ;) and use std::getline to parse the lines.
Reading entries into a vector is then (assuming you know the number of entries beforehand):
std::vector<Entry> readFromFile(std::istream& myfile,size_t n_entries) {
std::vector<Entry> entries;
data.resize( n_entries );
for (auto& e : entries ) myfile >> e;
return entries;
}

Data loss every time I push an object into a vector

I have a school project in which I am supposed to build a template class Vanzare meaning sale. I have 2 STL vectors, one for the cars that are in stock, and one for the cars that are sold, and 2 variables that count how many cars are in stock and how many cars are sold.
The class is supposed to have the -= operator overloaded, and it's supposed to sell a car, meaning deleting it from the stock vector (named stoc) and adding it to the sold vector (named vandut).
I also overloaded the += operator to add a car to stock.
The types of cars I have are all derived from the base class Masina, and some of them have extra fields.
The problem is that whenever I use += or -= ( or push_back() in general) for anything that has extra fields (compared to the base class), it breaks the previous elements from the vector. So I can't store any derived objects without loss of information.
I was also told that making a specialization of the whole class might help, but it didn't.
The template class starts around line 300 (I didn't know how to highlight it sorry). I am sorry if what I wrote wasn't clear, I am kinda new to all of this object oriented stuff. Thank you in advance!
Edit: I tried my best to slim the program down as much as I can, but it still has 250 lines of code. I also renamed (hopefully everything) to English. The field sh still gets lost when I add an object to the vector.
#include <iostream>
#include <string.h>
#include <vector>
#include <ctime>
using namespace std;
class Car
{
int capacity;
float length;
int price;
int year;
public:
int getYear();
void setPrice(int);
int getPrice();
Car();
~Car();
Car(int , float , int , int);
Car(const Car&);
friend istream& operator>>(istream& , Car&);
friend ostream& operator<<(ostream& , Car&);
friend class VAN;
};
int Car::getYear()
{
return year;
}
int Car::getPrice()
{
return price;
}
void Car::setPrice(int p)
{
price = p;
}
ostream& operator<<(ostream& out , Car& m)
{
out<<"capacity: "<<m.capacity<<"\nlength: "<<m.length<<"\nprice: "<<m.price<<"\nYear: "<<m.year;
return out;
}
istream& operator>>(istream& in , Car& m)
{
cout<<"capacity: ";
in>>m.capacity;
cout<<"length: ";
in>>m.length;
cout<<"price: ";
in>>m.price;
cout<<"Year: ";
in>>m.year;
return in;
}
Car::Car()
{
capacity = 0;
length = 0;
year = 0;
price = 0;
}
Car::Car(int lit , float lun , int an , int pre)
{
capacity = lit;
length = lun;
year = an;
price = pre;
}
Car::Car(const Car& m)
{
capacity = m.capacity;
length = m.length;
year = m.year;
price = m.price;
}
Car::~Car()
{
capacity = 0;
year = 0;
length = 0;
price = 0;
}
class VAN:public Car
{
int sh;
public:
void setSH(int);
int isSH();
VAN();
~VAN();
VAN(int , float , int , int , int);
VAN(const VAN&);
friend istream& operator>>(istream& , VAN&);
friend ostream& operator<<(ostream& , VAN&);
};
void VAN::setSH(int s)
{
if(s)
sh = 1;
else
sh = 0;
}
int VAN::isSH()
{
return sh;
}
ostream& operator<<(ostream& out , VAN& m)
{
out<<(Car&)m;
out<<endl<<"Second hand: "<<m.sh;
return out;
}
istream& operator>>(istream& in , VAN& m)
{
in>>(Car&)m;
cout<<"Second Hand: ";
int x;
in>>x;
if(x)
m.sh = 1;
return in;
}
VAN::VAN():Car()
{
;
}
VAN::~VAN()
{
;
}
VAN::VAN(int a , float b , int c, int d , int s):Car(a , b, c , d)
{
if(s)
sh = 1;
}
VAN::VAN(const VAN& m):Car(m)
{
;
}
template <class T>
class Sale
{
vector<T> stock;
vector<T> sold;
int nrSold;
int nrStock;
public:
Sale();
Sale<T>& operator += (T&);
template <class U>
friend ostream& operator<<(ostream& , Sale<U>& );
Sale<T>& operator -= (int);
};
template <class T> Sale<T>& Sale<T>::operator -= (int i)
{
nrStock--;
nrSold++;
sold.push_back(stock[i]);
stock.erase(stock.begin()+i);
time_t now = time(0);
tm *ltm = localtime(&now);
if(ltm->tm_mon == 5 || ltm->tm_mon == 6 || ltm->tm_mon == 7)
{
(sold[nrSold-1]).setPret((sold[nrSold-1].getPret()/10)*9);
}
return *this;
}
template <class T> Sale<T>::Sale()
{
nrSold = 0;
nrStock = 0;
}
template <class T> ostream& operator<<(ostream& out, Sale<T>& v)
{
out<<"\nWhat we have in stock:\n\n";
for(int i = 0; i < v.nrStock ; i++)
{
out<<v.stock[i]<<endl;
}
cout<<"\nWhat we sold:\n\n";
for(int i = 0; i < v.nrSold ; i++)
{
out<<v.sold[i]<<endl;
}
return out;
}
template <class T> Sale<T>& Sale<T>::operator += (T& t)
{
nrStock ++;
stock.push_back(t);
return *this;
}
int main()
{
VAN x;
cin>>x;
cout<<x;
Sale<VAN> v;
v += x;
v += x;
cout<<v;
}
There are logic errors in ALL of your VAN constructors:
in the default constructor, the sh member is not being initialized at all, so it's value is indeterminate.
In the conversion constructor, the sh member is not being initialized if the s parameter is 0, so it's value is indeterminate in that case. Your operator>> has a similar logic error that it is not updating the m.sh member if the input is 0.
in the copy constructor, the sh member is not being copied from m at all, so it's value is indeterminate.
When you push a VAN object into your vector, a copy of the object is made. And if the vector needs to reallocate its array to grow its capacity, new copies of existing elements are made. Since your copy constructor is broken, that is why you are losing your sh values.
Your VAN constructors need to look more like this instead 1:
VAN::VAN() : Car(), sh(0)
{
}
VAN::VAN(int a , float b , int c, int d , int s) : Car(a , b, c , d)
{
sh = (s != 0);
}
VAN::VAN(const VAN& m) : Car(m), sh(m.sh)
{
}
1: in the case of the copy constructor, you can actually omit it completely, and let the compiler auto-generate a suitable copy constructor for you.
And your operator>> needs to look more like this instead:
istream& operator>>(istream& in , VAN& m)
{
in >> (Car&)m;
cout << "Second Hand: ";
int x;
in >> x;
m.sh = (x != 0);
return in;
}
On a couple of side notes:
your sh member is declared as an int, but it clearly should be a bool instead.
VAN does not need to, and should not, be declared as a friend of Car. If VAN needs direct access to Car's private members, they should be declared as protected instead.

Reading a file into a vector of class objects

I have given it a few tries based on what I've read on numerous forums and books I have here. More details about this assignment here with more code from it: Click- another question from StackOverflow
What exactly I need to do is create a file with CHotel objects and insert them in this vector m_hoteli.
As to why it isn't working, it is either not reading the string from the file and it's not filling the vector at all.
This is my file:
"Marina" 5 500
"Tulip" 4 400
"BlackSea" 3 300
"SwissBell" 5 600
class CComplex:CHotel
{
protected:
string m_complex;
vector<CHotel> m_hoteli;
public:
CComplex(){};
CComplex(string filename, string nComplex)
{
fstream file("filename.txt", ios::in);
CHotel temp(" ",0,0);
while (file >> temp)
{
m_hoteli.push_back(temp);
}
/* Second try:
m_complex = nComplex;
fstream in;
in.open(filename, ios::in);
string s;
while (getline(in, s))
{
CHotel h1(s);
m_hoteli.push_back(h1);
}
Third try:
m_complex = nComplex;
ifstream iStream(filename);
if (iStream.good())
{
copy(istream_iterator<CHotel>(iStream), istream_iterator<CHotel>(), back_inserter(m_hoteli));
}
}
*/
}
That's the CHotel code:
class CHotel : public CTurist
{
protected:
string hName;
int stars;
int beds;
map<CTurist, unsigned> Turisti;
public:
unsigned Sum = 0;
int br = 0;
CHotel(){};
CHotel(string hName2, int zvezdi, int legla)
{
hName = hName;
stars = zvezdi;
beds = legla;
}
friend istream& operator>>(std::istream& is, CHotel& e)
{
is >> e.hName >> e.stars >> e.beds;;
return is;
}
I just do this in the main: CComplex c1("filename.txt", "Complex1");
You are not using the filename parameter in the CComplex constructor, other than that your code should work.
fstream file(filename, ios::in);
Do you know how to step debug? Here is some info on debugging
Here is your full code, the only change is the filename parameter and the file should now be placed in c:\temp.
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <fstream>
using namespace std;
class CTurist
{
protected:
string tName;
int age;
public:
CTurist() {};
CTurist(string name, int age2)
{
tName = name;
age = age2;
}
bool operator<(const CTurist& e) const
{
return age < e.age;
}
friend ostream& operator<<(ostream& os, const CTurist&& e);
friend ifstream& operator>>(ifstream& is, CTurist&& e);
};
class CHotel : public CTurist
{
protected:
string hName;
int stars;
int beds;
map<CTurist, unsigned> Turisti;
public:
unsigned Sum = 0;
int br = 0;
CHotel() {};
CHotel(string hName2, int zvezdi, int legla)
{
hName = hName;
stars = zvezdi;
beds = legla;
}
friend istream& operator>>(std::istream& is, CHotel& e)
{
is >> e.hName >> e.stars >> e.beds;;
return is;
}
};
class CComplex :CHotel
{
protected:
string m_complex;
vector<CHotel> m_hoteli;
public:
CComplex() {};
CComplex(string filename, string nComplex)
{
fstream file(filename, ios::in);
CHotel temp(" ", 0, 0);
while (file >> temp)
{
m_hoteli.push_back(temp);
}
}
};
int main()
{
CComplex c1("C:\\temp\\file.txt", "Complex1");
system("pause");
return 0;
}
Try putting a breakpoint in main and step through your program with f11 and f10

C++ preprocessor and operator + overloading

I want to use the C++ preprocessor to be able to write the following in any C++ block.
class Student {
private:
int age;
int grade;
int courses;
}
int main(){
CREATE_STUDENT 15+62+2 ;
}
The previous code will create a Student with these 3 members.
I want to use + operator overloading.
Any idea of how to do it?
I want EXACTLY the syntax I mentioned above.
Why not just use a constructor:
class Student {
private:
int age;
int grade;
int courses;
public:
Student(int a, int g, int c)
{
age = a;
grade = g;
courses = c;
}
}
int main(){
Student s(15,62,2);
}
Well, I completely fail to understand why you would want to do such a thing. But it is possible, sorta.
You'll need to make it a bit more complex than that to be able to use more than one such "construct" in the same block though.
#include <iostream>
#define GRADE_STUDENT Student student = (Student)
class Student {
public:
Student(int a): age(a), grade(-1), courses(-1), setup(0) {};
Student& operator+(int p)
{
switch(setup) {
case 0: grade = p; break;
case 1: courses = p; break;
default: /* die */ char *p=0; *p=0;
}
setup++;
return *this;
};
void print()
{
std::cout << age << ", " << grade << ", " << courses << std::endl;
};
private:
int age;
int grade;
int courses;
int setup;
};
int main()
{
{
GRADE_STUDENT 15+62+2 ;
student.print();
}
{
GRADE_STUDENT 15+62 ;
student.print();
}
{
GRADE_STUDENT 15+62+2+3 ; // crash
}
return 42;
}
You should template your class instead of working with the preprocessor.