I/O overloading and reading from text files - c++

I need to define a read and a print function for a class that has an array of objects as a private variable. I have to read in objects from a text file and print them to the screen. To do this I need to overload the << and >> operators. I understand I need to use loops to read and print the information stored in the array but I'm not sure how to accomplish this. My lecturer has given us a skeleton code which is basically function prototypes and the main function which I need to stick to. I understand how this works with public structs as I have done this exact scenario using that but the private variables of class' are tripping me up.
class EmployeeList {
public:
//Constructors
EmployeeList();
EmployeeList(istream&);
//Accessors
bool isEmpty() const;
bool isFull() const;
int size() const; //Number of employees in list
Employee item(int i) const; //i'th employee
//Mutators
void setItem(int i,const Employee& e);
//I/O functions, sets the i'th emplyee to e
void read(istream&);
void print(ostream&) const;
private:
enum {MAXSIZE = 100};
Employee list[MAXSIZE];
int count; //Number of employees in the current list
};
EmployeeList::EmployeeList() {
count = 0;
}
EmployeeList::EmployeeList(istream& in) {
//list[MAXSIZE] = in;
}
bool EmployeeList::isEmpty() const {
return (count == 0);
}
bool EmployeeList::isFull() const {
return (count == MAXSIZE);
}
int EmployeeList::size() const {
return count;
}
Employee EmployeeList::item(int i) const {
}
void EmployeeList::setItem(int i, const Employee& e) {
}
void EmployeeList::read(istream& in) {
Employee tempList;
while (in >> tempList) {
}
}
void EmployeeList::print(ostream& out) const {
for (int i=0; i < size(); i++) {
}
cout << out;
}
The above part is the Class EmployeeList while the below part are overloading functions. The commented out parts are ideas that I thought might work but didn't.
istream& operator>>(istream& in, EmployeeList& l) {
l.read(in);
return in;
}
ostream& operator<<(ostream& out, const EmployeeList& l) {
l.print(out);
return out;
}
Below is the main function given to us.
int main() {
authorInfo();
ifstream infile("a1in.txt");
if(!infile) {
cout << "file 'alin.txt' not found.";
return EXIT_FAILURE;
}
EmployeeList theList(infile);
cout << endl;
cout << theList.size() << " employees read:\n" << theList << endl;
process(theList);
return EXIT_SUCCESS;
}
Hope someone can steer me in the right direction! Let me know if you need more of the code. Thanks!
EDIT:
Employee read and print functions:
void Employee::read(istream& in) {
in >> name >> id >> salary;
}
void Employee::print(ostream& out) const {
out << getName() <<" "<< getID() <<" "<< getSalary() << endl;
}
Employee overloading:
istream& operator>>(istream& in, Employee& e) {
e.read(in);
return in;
}
ostream& operator<<(ostream& out, const Employee& e) {
e.print(out);
return out;
}
EDIT 2: Updated read() function. The line with the while is where the error is.
void EmployeeList::read(istream& in) {
Employee inEmployee;
while (in >> inEmployee && count < MAXSIZE) {
list[count] = inEmployee;
count++;
}
}
EDIT 3: Here is the print() function I have so far. It does indeed print but I get the default constructor information rather than information from the file. Is this a read or print function issue? I'm thinking read function still.
void EmployeeList::print(ostream& out) const {
cout << endl;
for (int i=0; i < count; i++) {
out << list[count];
}
}

Array Bounds
In your class, you have:
Employee list[MAXSIZE];
Given this, there is an error the code you tried:
EmployeeList::EmployeeList(istream& in) {
list[MAXSIZE] = in;
}
list only has elements from list[0] to list[MAXSIZE - 1]. list[MAXSIZE] is one past the end of the array, and is invalid.
Constructors
That said, I'd strongly recommend against having a constructor that takes an istream&. It is far better to construct an empty object with the default constructor, then use its read(istream&) method (via operator <<) to load the data. In other words, rather than:
EmployeeList theList(infile);
use:
EmployeeList theList;
infile >> theList;
If you're required to have a constructor that takes an istream&, just have it call read() after initializing the object:
EmployeeList::EmployeeList(istream& in): count(0) {
read(in);
}
Note that only one constructor is called, so the initialization in EmployeeList::EmployeeList() does not happen in EmployeeList::EmployeeList(istream&). I hear the new version of C++ deals with this unnecessary repetition, but for the time being that's where we are.
Naming
Another thing: your code will be less confusing with better variable names. In this case:
void EmployeeList::read(istream& in) {
Employee tempList;
while (in >> tempList) {
}
}
Don't say tempList because it's not a "temporary list", it's a single Employee that has been read. Better would be:
void EmployeeList::read(istream& in) {
Employee inEmployee;
while (in >> inEmployee) {
list[count++] = inEmployee;
}
}

This looks like a homework so i'll try to just give you a hint:
void EmployeeList::read(istream& in) {
Employee tempList;
while (in >> tempList) {
//here you are creating a tempList so after you fill in the values in tempList
//the tempList is to become a part of Employee list[MAXSIZE];
}
}
and how do you fill in the values? You do this using your constructor and maintaining the count
EmployeeList::EmployeeList(istream& in) {
//here...
}

You could start off by figuring out how to read in input. The approach, which is likely incomplete, that I would take is this:
EmployeeList::EmployeeList(istream& in) {
count = 0;
read(in); // delegate it to avoid duplication
}
void EmployeeList::read(istream& in) {
Employee tempList;
while (in >> tempList && count < MAXSIZE) {
list[count] = tempList;
++count;
}
}
You will need to overload operator>> for Employee class for this to work.

Here is how I would write this, without the skeleton constraint. Feel free to adapt to your assignment requirements.
Source: http://www.ideone.com/R9EeF
Iostreams are hard to master. You have to read about std::getline, the std::ios flags and stringstreams to understand how to parse an employee list with them.
I prefer giving you a working template (that you cannot use for your assignment since I don't make use of the skeleton at all), since there is a lot to say about iostreams.
Also feel free to ask questions, so that I can enhance my answer with your actual problems.

Related

How to remove certain specified value from vectors?

mySongs is a vector that store a collection of songs input by the user. In the if statement, the program will check the elements in the vector with user input. If match, it will delete that specified value from the vector. When I look for the solution, I see someone recommend to use remove/erase idiom:. But when I implement in my code, it continue pop up this error C2678 binary '==': no operator found which takes a left - hand operand of type 'Song' (or there is no acceptable conversion)
void deleteSong() {
string songTitle;
cout << "\n\t\tPlease enter the particular song name to remove: ";
cin >> songTitle;
if (songTitle != "") {
for (Song songs : mySongs) {
if (songs.title == songTitle) {
mySongs.erase(find(mySongs.begin, mySongs.end, songs)); //erase an element with value
break;
}
}
}
}
This error is due to the fact that the class Song does not have an == operator. This can be solved in one of the two following ways
If you have access to the source code of Song then
add the following function to it. please note that const is necessary
bool operator == ( const Song& song)
{
//Do the comparison code here
}
If you don't have access to the source code. Then add the following function
bool operator == (const Song& song1, const Song& song2)
{
// Do the comparison code here
}
That said, there is another minor problem with your code
The erase function should be called like this
mySongs.erase(find(mySongs.begin(), mySongs.end(), songs));
I decided to add a minimal example to help you. In that example I assumed a certain implementation of Song, but that implementation does not have to be what you have
#include <iostream>
#include<vector>
#include<algorithm>
#include<string>
using namespace std;
class Song
{
public:
std::string title;
/* bool operator ==(const Song& other)
{
return this->title == other.title;
}*/
};
bool operator ==(const Song& song1,const Song& other)
{
return song1.title == other.title;
}
std::vector<Song> mySongs={{"ali"},{"ahmed"},{"ali"}};;
void deleteSong() {
string songTitle;
cout << "\n\t\tPlease enter the particular song name to remove: ";
cin >> songTitle;
if (songTitle != "") {
for (const auto& songs : mySongs) {
if (songs.title == songTitle) {
mySongs.erase(find(mySongs.begin(), mySongs.end(), songs)); //erase an element with value
break;
}
}
}
}
int main() {
// your code goes here
return 0;
}

vector<string> doesnt work when using seperate classes

I have a txt file which has a rogue-like level
I load it like so:
void File::LoadLevel()
{
ifstream input_file;
input_file.open("Level_1.txt");
if (input_file.fail())
{
perror("Level_1.txt");
}
while (input_file >> _level)
{
_level_instance.push_back(_level);
}
}
variables:
string _level;
vector<string> _level_instance;
I print it out like this:
for (int i = 0; i < _level_instance.size(); i++)
{
cout << _level_instance[i] << endl;
}
which works fine.
however I have a vector in another class as well and i use a getter like this:
vector<string>GetlevelData(){ return _level_data; }
and I change the LoadLevel() from this:
_level_instance.push_back(_level);
to this:
Level Lvl;
Lvl.GetLevelData().pushback(_level);
I make an method in 'Level' class which prints it out to the screen same as before
it compiles but it doesnt print out anything why?
By the way in the int main() neccesary methods are executed which is OpenLevel() from File class and Print() from Level class
EDIT:
passing it &by reference didnt work
here are both methods in Level.h:
void SetLevelData(const std::string &string) {
_level_data.push_back(string);
}
//Getters
vector<string>& GetlevelData(){ return _level_data; }
in File.cpp:
void File::LoadLevel()
{
ifstream input_file;
Level lvl;
input_file.open("Level_1.txt");
if (input_file.fail())
{
perror("Level_1.txt");
}
while (input_file >> _level)
{
lvl.GetlevelData().push_back(_level);
}
}
it doesnt work it prints nothing, even though i added '&'
the result is same when i try it with 'push_back' method in Level.h
Here is int main() just in case:
int main()
{
File f;
Level lvl;
f.LoadLevel();
lvl.PrintLevel();
system("PAUSE");
return 0;
}
And PrintLevel() in Level.cpp:
void Level::PrintLevel()
{
for (int i = 0; i < _level_data.size(); i++)
{
cout << _level_data[i] << endl;
}
}
The GetlevelData function returns its vector by value which means a whole new copy of it is created. Anything you push into that vector will be lost when the copy goes out of scope (which happens when the expression Lvl.GetLevelData().pushback(_level) is done).
You should return it by reference instead:
vector<string>& GetlevelData(){ return _level_data; }
// ^
// |
// Return by reference
vector<string>GetlevelData() returns a copy of the vector and not a reference to it.
So your Lvl.GetLevelData().push_back(_level); returns a copy adds data to the vector and then deletes that copy again. So you add it to a vector that will be immediately be deleted again.
You would need to return it either by reference:
vector<string>& GetlevelData(){ return _level_data; }
Or write a method to push back the data:
void push_back( const std::string &string) {
_level_data.push_back(string);
}

Creating a class while using cascading and PLP

So I am trying to create a class ( Version ) with data members. The member functions are setters ( allow for cascading calls ) and getters ( use PLP and handle constant objects ). In the int main, it allows the user to input the numbers, then it needs to use cascading member functions calls for all data members in one statement and display the version by calling the getters and have them return just the value of the data member.
I pretty much coded everything, but I feel like I'm missing a step or doing something wrong with the parameters. My error says that I need a ')' for my setters, but I feel like it is a different problem. I did not get all my notes for how to do this, so can someone tell me what I am missing or need to fix? Thanks!
#include<iostream>
using namespace std;
class Version
{
private:
int major, minor, service_pack;
public:
Version();
Version& setMajor(int maj);
Version& setMinor(int min);
Version& setService_pack(int sp);
int getMinor(Version *const this);
int getMajor(Version* const this);
int getService_pack(Version* const this);
};
Version::Version()
{
major = 0;
minor = 0;
service_pack = 0;
}
Version& Version::setMinor(int min)
{
minor = min;
return *this;
}
Version& Version::setMinor(int maj)
{
major = maj;
return *this;
}
Version& Version::setMinor(int sp)
{
service_pack = sp;
return *this;
}
int Version::getMinor(Version* const this)
{
return this->minor;
(*this).minor;
}
int Version::getMajor(Version* const this)
{
return this->major;
(*this).major;
}
int Version::getService_pack(Version* const this)
{
return this->service_pack;
(*this).service_pack;
}
int main()
{
int minor, major, service_pack;
Version a;
cout << "Enter minor, major, and service pack: " << endl;
cin >> minor, major, service_pack;
a.setMinor(minor).setMajor(major).setService_pack(service_pack);
cout << "Major: " << a.getMajor() << "Minor: "<< a.getMinor << "Service Pack: " << a.getService_pack();
system("PAUSE");
}
You are missing parenbtheses in main() for diplaying a.getMinor(). Without the parentheses, you'd refer to the member function itself.
Your getters should in principle take no arguments if you return the value. In any case, avoid passing arguments called this.
One further improvement you could make, would be to declare the getters as const:
int Version::getMinor() const
{
return minor; // this-> is only needed to disambiguate
}

Matching constructor not found (C++) in HardwareStore Class

I have been working on a hardware store application, where there is a HardwareRecordclass that stores information about each object in a store (ex: nuts, bolts, screws, and so forth).
The information is stored in a ".dat" file, but this is not important right now.
Here's my declaration of this class:
// Definition of HardwareRecord class
#ifndef __Initialize_Hardware_Store_File__HardwareRecord__
#define __Initialize_Hardware_Store_File__HardwareRecord__
#include <iostream>
class HardwareRecord
{
public:
HardwareRecord(const int& account=0,const std::string& name="",const std::string& description="",const double& price=0.0); //constructor
HardwareRecord operator=(HardwareRecord&);
//'set' and 'get' functions
void setAccountNumber(int);
int getAccountNumber() const;
void setName(std::string);
std::string getName() const;
void setPrice(double);
double getPrice() const;
void setDescription(std::string);
std::string getDescription() const;
void wipeRecord(); //set everything to blank
private:
int myAccountNumber;
std::string myName;
std::string myDescription;
double myPrice;
};
#endif /* defined(__Initialize_Hardware_Store_File__HardwareRecord__) */
Here's my class definition:
// Implementation of HardwareRecord class definition
#include <iostream>
#include "HardwareRecord.h"
using namespace std;
HardwareRecord HardwareRecord::operator=(HardwareRecord & aRecord)
{
this->myAccountNumber=aRecord.myAccountNumber;
this->myName=aRecord.myName;
this->myDescription=aRecord.myDescription;
this->myPrice=aRecord.myPrice;
return *this; //allow for cascaded overloading
}
HardwareRecord::HardwareRecord(const int& account,const string& name,const string&
description,const double& price)
{
setAccountNumber(account);
setName(name);
setPrice(price);
setDescription(description);
}
void HardwareRecord::wipeRecord()
{
setAccountNumber(0);
setName("");
setPrice(0);
setDescription("");
}
void HardwareRecord::setAccountNumber(int num)
{
if (num < 0)
{
throw invalid_argument("The account number is not in the valid range (greater or equal to 0)");
}
else
{
myAccountNumber=num;
}
}
int HardwareRecord::getAccountNumber() const
{
return myAccountNumber;
}
void HardwareRecord::setName(string name)
{
myName=name;
}
string HardwareRecord::getName() const
{
return myName;
}
void HardwareRecord::setPrice(double price)
{
if (price < 0)
{
throw invalid_argument("The price can not be less than zero");
}
else
{
myPrice=price;
}
}
double HardwareRecord::getPrice() const
{
return myPrice;
}
void HardwareRecord::setDescription(string description)
{
this->myDescription=description;
}
string HardwareRecord::getDescription() const
{
return myDescription;
}
The class described is supposed to be used in the following main.cpp file:
// Application that models a store's record of inventory
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cstdlib>
#include <sstream>
#include "HardwareRecord.h" //HardwareRecord definition
using namespace std;
//enumeration of choices
enum Choices {WIPE_RECORDS,UPDATE,LIST,PRINT,DELETE,NEW,END,LAST};
std::ostream& operator<<(std::ostream& op,const Choices& choices)
{
//print the string corresponding to the value of enum type Choices
string output="";
switch (choices)
{
case WIPE_RECORDS:
output = "wipe records";
break;
case UPDATE:
output = "update records";
break;
case LIST:
output = "list records";
break;
case PRINT:
output = "print records";
break;
case DELETE:
output = "delete records";
break;
case NEW:
output = "add new record";
break;
case END:
output = "terminate application";
break;
case LAST:
output = "an option used to iterate over the values in the Choice enumeration";
break;
default:
cerr << "Error. invalid value is read";
exit(EXIT_FAILURE);
break;
}
op << output; //print output
return op;
}
//prototype of helper functions
int enterChoice();
void wipeRecords(fstream&);
void updateRecord(fstream&);
void listRecords(fstream&);
void createTextFile(fstream&);
void deleteRecord(fstream&);
void newRecord(fstream&);
int main()
{
//open file for reading and writinbg
fstream outRecord ("HardwareRecord.dat",ios::in|ios::out|ios::binary);
//exit program if fstream cannot open file
if (!outRecord)
{
cerr << "File could not be opened." << endl;
exit(EXIT_FAILURE);
}
int choice; //user's choice
//enable user to specify action
while ((choice=enterChoice()) !=END)
{
switch (choice)
{
case WIPE_RECORDS: //wipe all records clean
wipeRecords(outRecord);
break;
case UPDATE: //update a record
updateRecord(outRecord);
break;
case LIST: //list all current records
listRecords(outRecord);
break;
case PRINT: //print a record
createTextFile(outRecord);
break;
case DELETE: //delete a record
deleteRecord(outRecord);
break;
case NEW: //add a new record (if space allows)
newRecord(outRecord);
break;
default: //display error if user does not select valid choice
cerr << "Incorrect choice" << endl;
}
outRecord.clear();
}
return 0;
}
//enable user to input menu choice
int enterChoice()
{
//display avaliable options
cout << "\nEnter your choice:\n"<< endl;
Choices aChoice;
for (int c=WIPE_RECORDS; c < LAST; c++)
{
aChoice= (Choices) c;
cout << c << " - " << aChoice << endl;
}
cout << "\n?: ";
int menuChoice;
cin >> menuChoice;
return menuChoice;
}
void wipeRecords(fstream& theFile)
{
HardwareRecord temp;
for (int i=0; i < 100;i++)
{
//convert record from binary and assign to temp
//make temp "wipe itself"
}
}
Yes, I realize that many of the functions are defined by prototype, but are not actually declared. This will be done later after this very problem described shortly afterwards is fixed. Please direct your attention to the following piece of code, from this very file:
void wipeRecords(fstream& theFile)
{
HardwareRecord temp; //Here's where the error occurs: No Matching constructor!
for (int i=0; i < 100;i++)
{
//convert record from binary and assign to temp
//make temp "wipe itself"
}
}
Whenever I try to compile this project on my Mac (I use xCode), I get the following error for the line that is commented. The error is "No matching constructor for initialization of 'HardwareRecord'". However, I provide default values for the constructor of a HardwareRecord object, so the line
HardwareRecord temp;
should initialize without any problems.
What is going on? How can I fix this?
I think this is the problem. In your constructor, you use an std::string&, however, you never include <string> in your code!
Along with some other errors, compiling with G++ gives you:
prog.cpp:46:57: error: ‘string’ does not name a type
This may invalidate your default constructor.
HardwareRecord class should have default constructor, e.g., like this:
class HardwareRecord {
public:
HardwareRecord() : myAccountNumber(0),
myName(""),
myDescription(""),
myPrice(0.0f) {}
...
};
Your current constructor with default parameter values doesn't seem to be regarded as a 'Default Constructor'.
You're attempting to use a default constructor.
HardwareRecord temp;
But you've declared a constructor that takes arguments, as well as an assignment operator. The act of declaring a constructor that takes arguments means that the compiler will not generate a default constructor for you. So if you want one you'll have to declare one yourself OR use the constructor you've created.
EDIT
My answer above is NOT correct as the OP has provided defaults for all specified arguments.
In the case I specified the compiler will state something like:
error: no matching function for call to ‘Foo::Foo()’
But with defaults specified it will compile.
Small test:
-- foo.h
class Foo
{
private:
int _i;
public:
Foo( int i );
};
-- foo.cpp
#include "foo.h"
Foo::Foo( int i )
: _i( i )
{
}
int main()
{
Foo foo;
return 0;
}

Confused about using constructurs with istream parameters

I have a slight problem when trying to learn how to create constructors.
I am currently learning c++ using the "C++ Primer" book and I've come to a point where I am told to create some constructors then change a code using these constructors. The exercise states that I should rewrite this program using the istream constructor but I don't know how to do this.
int main()
{
Sales_data total;
if (read(cin,total))
{
Sales_data trans;
while (read(cin,trans))
{
if (total.isbn() == trans.isbn())
{
total.combine(trans);
}
else
{
print(cout, total) << endl;
total = trans;
}
}
print(cout, total) << endl;
}
else
{
cerr << "No data?!" << endl;
}
return 0;
}
The problem I have is, I have no idea how I am supposed to use an constructor using the istream, i thought it would be simple and just pass in cin as a default value but it does not work. from visual studios I get a "LNK2019" error and from code::blocks "undefined reference to Sales_data::read(std::istream&, Sales_data&)
My code in my header file looks like this:
struct Sales_data
{
Sales_data() = default;
Sales_data(const std::string &s) : bookNo(s){}
Sales_data(const std::string &s, unsigned n, double p) :
bookNo(s), units_sold(n), revenue(p*n){}
Sales_data(std::istream &is)
{
read(is, *this);
}
std::string isbn() const { return bookNo; };
Sales_data& combine(const Sales_data&);
double avg_price() const;
Sales_data add(Sales_data&, Sales_data&);
std::ostream &print(std::ostream&, const Sales_data&);
std::istream &read(std::istream&, Sales_data&);
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
plus some definitions below.
and my cpp file looks like this:
int main()
{
Sales_data total(cin); //results in error "LNK2019" or "undefined reference to Sales_data::read(std::istream&, Sales_data&)"
if (1)
{ //not really sure what to use here but if I get my default value to work I might figure it out.
// I'm thinking it should work with just cin >> total or read(total)
Sales_data trans(cin); //results in error "LNK2019" or "undefined reference to Sales_data::read(std::istream&, Sales_data&)"
while (1)
{
if (total.isbn() == trans.isbn())
{
total.combine(trans);
}
else
{
print(cout, total) << endl;
total = trans;
}
}
print(cout, total) << endl;
}
else
{
cerr << "No data?!" << endl;
}
return 0;
}
I hope you understand my problem, and I appreciate all the help you provide! :)
It sounds to me as if you're either missing some code from your book, or the book is expecting you to implement other functions and not just the constructor.
The linker error is telling you that it's unable to find an implementation of your read function, which should look like:
std::istream& Sales_data::read(std::istream&, Sales_data&)
{
// TODO - implementation here.
}
It's also worth mentioning that function implementations should be added to the source (.cpp) file.