How do you create multiple new class objects using the default class constructor?
For a project I am having to write a program that writes three class objects into a file. That I have done... The next part is being able to read the data back into three separate class objects using a readData function and then displaying the data. I am completely lost at how to do this so I don't have any code in the readData function.
Here is an example of what the object looks like when it is being written to the file.
employee name(21, "first last", "45 East State", "661-9000", 30, 12.00);
Here is the bulk of my code the employee class is fairly basic but here is the default class constructor.
employee::employee ();
employee::employee(int locEmpNumber, string locName, string locaddress, string locphone, double locHrWorked, double locHrWage)
#include "employee.h"
#include <string>
#include <iomanip>
#include <iostream>
#include <fstream>
using namespace std;
void writeData (const employee& e);
void readData (const employee& e);
void printCheck (const employee& e);
int main( )
{
//Declarations
const int ONE = 1;
const int TWO = 2;
int userInput;
cout << "This program has two options:" << endl;
cout << "1 - Create a data files called 'EmployeeInfo.txt', or" << endl;
cout << "2 - Read data from a file and print paychecks." << endl;
cout << "Please enter (1) to create a file or (2) to print checks: ";
cin >> userInput;
if (userInput == ONE)
{
//Create employee objects:
employee joe(37, "Joe Brown", "123 Main St.", "123-6788", 45, 10.00);
employee sam(21, "Sam Jones", "45 East State", "661-9000", 30, 12.00);
employee mary(15, "Mary Smith", "12 High Street", "401-8900", 40, 15.00);
ofstream empFile ("EmployeeInfo.txt");
//Employee objects to write themselves out to the file.
writeData(joe);
writeData(sam);
writeData(mary);
//Close the file.
empFile.close();
//Print an message that creation of the file is complete.
system("CLS");
cout << "\nCreation of 'EmployeeInfo.txt' has completed.\n";
cout << "\nYou can now run option 2.\n";
//Exit.
system("PAUSE");
return 0;
}
else if (userInput == TWO)
{
//Create three new Employee objects, using the default Employee constructor.
//Open the file that you just saved.
//Have each object read itself in from the file.
//Call the printCheck( ) function for each of the three new objects, just as you did in the previous project.
}
else
{
system("CLS");
cout << "Incorrect entry.... Please try again and follow directions closely! \n" << endl;
system("PAUSE");
return 0;
}
}
void writeData(const employee& e)
{
fstream empFile;
empFile.open ("EmployeeInfo.txt", ios::app);
empFile << e.getEmpNumber() << "\n";
empFile << e.getName() << "\n";
empFile << e.getAddress() << "\n";
empFile << e.getPhone() << "\n";
empFile << e.getHrWorked() << "\n";
empFile << e.getHrWage() << "\n";
}
void readData(const employee& e)
{
fstream empFile;
empFile.open ("EmployeeInfo.txt", ios::in);
if(empFile.fail())
{
cout << "File could not be open. Please try option 1 then option 2.\n" << endl;
return;
}
}
It's good to see that you have made an effort at solving the problem. However, there is a mismatch between what you set out in your question and some of the comments in your code. It seems clear to me that a key part of your brief is that the employee object itself is required to be able to write itself to the file and to read itself back from the file.
You have written code that will write the contents of the object to the file rather than having the object write itself to the file. It might seem like I'm splitting hairs on this one, but this is the essence of what Object Oriented Programming is about. Encapsulating the functionality within the object itself is the real goal here.
I've included some code below to help you. Hopefully this will make good sense for you.
class employee
{
private:
int _locEmpNumber;
std::string _locName;
std::string _locAddress;
std::string _locPhone;
double _locHrWorked;
double _locHrWage;
public:
employee();
employee(int locEmpNumber, std::string locName, std::string locAddress, std::string locPhone, double locHrWorked, double locHrWage);
//object should be able to save itself as per your project brief.
void writeData(std::ofstream &empFile);
//object should be able to read itself from file as per your project brief
void readData(std::ifstream &empFile);
};
employee::employee()
{
_locEmpNumber = 0;
_locHrWorked = _locHrWage = 0;
}
employee::employee(int locEmpNumber, std::string locName, std::string locAddress, std::string locPhone, double locHrWorked, double locHrWage)
{
_locEmpNumber = locEmpNumber;
_locName = locName;
_locAddress = locAddress;
_locPhone = locPhone;
_locHrWorked = locHrWorked;
_locHrWage = locHrWage;
}
//
//From what I can glean from your brief ...
//Employee objects to write themselves out to the file!!!
void employee::writeData(std::ofstream &empFile)
{
empFile << _locEmpNumber << std::endl;
empFile << _locName << std::endl;
empFile << _locAddress<< std::endl;
empFile << _locPhone << std::endl;
empFile << _locHrWorked << std::endl;
empFile << _locHrWage << std::endl;
}
//
//Again, from what I can glean from your brief ...
//Have each object read itself in from the file!!!
void employee::readData(std::ifstream &empFile)
{
//Normally you would have error handling in a method like this and
//would either return a response that indicates that the operation
//succeded or failed. You might alternatively use exception handling
//or indeed a combination of both.
//
//Normally you would reset all members to initial / empty values before
//reading values into them from your file. In this case we will omit that
//for the purposes of simplicity. The main reason you would reset members
//is to ensure that when reusing an object you don't end up with partial
//data from the current "read" operation mixed with partial data that
//was already in the object before you started reading.
std::string inputStr;
std::getline(empFile, inputStr);
_locEmpNumber = atoi(inputStr.c_str());
std::getline(empFile, _locName);
std::getline(empFile, _locAddress);
std::getline(empFile, _locPhone);
std::getline(empFile, inputStr);
_locHrWorked = atof(inputStr.c_str());
std::getline(empFile, inputStr);
_locHrWage = atof(inputStr.c_str());
}
int main(int argc, char* argv[])
{
//Declarations
const int ONE = 1;
const int TWO = 2;
int userInput;
std::cout << "This program has two options:" << std::endl;
std::cout << "1 - Create a data files called 'EmployeeInfo.txt', or" << std::endl;
std::cout << "2 - Read data from a file and print paychecks." << std::endl;
std::cout << "Please enter (1) to create a file or (2) to print checks: ";
std::cin >> userInput;
if (userInput == ONE)
{
//Create employee objects:
employee joe(37, "Joe Brown", "123 Main St.", "123-6788", 45, 10.00);
employee sam(21, "Sam Jones", "45 East State", "661-9000", 30, 12.00);
employee mary(15, "Mary Smith", "12 High Street", "401-8900", 40, 15.00);
std::ofstream empFile ("EmployeeInfo.txt");
//Employee objects to write themselves out to the file.
joe.writeData(empFile);
sam.writeData(empFile);
mary.writeData(empFile);
// writeData(joe);
// writeData(sam);
// writeData(mary);
//Close the file.
empFile.close();
//Print an message that creation of the file is complete.
system("CLS");
std::cout << "\nCreation of 'EmployeeInfo.txt' has completed.\n";
std::cout << "\nYou can now run option 2.\n";
//Exit.
system("PAUSE");
return 0;
}
else if (userInput == TWO)
{
//Create three new Employee objects, using the default Employee constructor.
employee joe;
employee sam;
employee mary;
//Open the file that you just saved.
std::ifstream empFile("EmployeeInfo.txt");
//Have each object read itself in from the file.
joe.readData(empFile);
sam.readData(empFile);
mary.readData(empFile);
empFile.close();
//Call the printCheck( ) function for each of the three new objects, just as you did in the previous project.
//I'll leave it to you to add this yourself.
}
else
{
system("CLS");
std::cout << "Incorrect entry.... Please try again and follow directions closely! \n" << std::endl;
system("PAUSE");
return 0;
}
return 0;
}
Related
The output of the code show gibberish values for all the variables of the Student struct. When the display function is ran.
I've include the relevant code in each of the add and display function for the binary file.
For the second function, does the seekg pointer automatically move to read the the next record each time the for loop runs?
//Student struct
struct Student
{
char name [30];
float labTest;
float assignments;
float exam;
};
//Writing function
afile.open(fileName,ios::out|ios::binary);
Student S;
strcpy(S.name,"test");
S.labTest = rand()%100+1;
S.assignments = rand()%100+1;
S.exam = rand()%100+1;
afile.write(reinterpret_cast<char*>(&S),sizeof(S));
afile.close();
//Reading function
afile.open(fileName,ios::in|ios::binary);
afile.seekg(0,ios::end);
int nobyte = afile.tellg();
int recno = nobyte / sizeof(Student);
Student S;
//Loop and read every record
for(int i = 0;i<recno;i++)
{
afile.read(reinterpret_cast<char*>(&S),sizeof(S));
cout << "Name of Student: " << S.name << endl
<< "Lab mark: " << S.labTest << endl
<< "Assignment mark: " << S.assignments << endl
<< "Exam mark: " << S.exam << endl << endl;
}
afile.close();
There are a lot of problems with your code:
Calling your write function will permanently overwrite the last written data set. You have to add: ios::append, so that new data will be written behind the last data you wrote before.
After you move with afile.seekg(0,ios::end); to get with tellg the file size, you have to go back to the start of the file before reading with afile.seekg(0,ios::beg)
It looks that you use a char array to store a string. This is not c++ style! And it is dangerous how you use it. If you use strcpy, you can copy a string which is longer than the space you reserved for it. So you should prefer std::string for that. But you can't simply write a struct which constains std::string as binary! To get checked copy you can use strncpy, but that is still not c++ ;)
For the second function, does the seekg pointer automatically move to read the the next record each time the for loop runs?
Yes, the file position moves which each successful read and write.
A general remark writing binary data by simply dumping memory content:
That is not a good idea, because you can only read that data back, if you use the same machine type and the same compiler options. That means: A machine with different endianness will read data totally corrupted. Also a different integer type ( 32 bit vs 64 bit ) will break that code!
So you should invest some time how to serialize data in a portable way. There are a lot of libraries around which can be used to read/write also complex data types like std::string or container types.
A hint using SO:
Please provide code which everybody can simply cut and paste and compiled. I did not know what your Student struct is. So I take a lot of assumptions! Is your struct really using char[]? We don't know!
#include <iostream>
#include <fstream>
#include <cstring>
const char* fileName="x.bin";
struct Student
{
char name[100]; // not c++ style!
int labTest;
int assignments;
int exam;
};
// Writing function
void Write()
{
std::ofstream afile;
afile.open(fileName,std::ios::out|std::ios::binary|std::ios::app);
Student S;
strcpy(S.name,"test"); // should not be done this way!
S.labTest = rand()%100+1;
S.assignments = rand()%100+1;
S.exam = rand()%100+1;
afile.write(reinterpret_cast<char*>(&S),sizeof(S));
afile.close();
}
void Read()
{
//Reading function
std::ifstream afile;
afile.open(fileName,std::ios::in|std::ios::binary);
afile.seekg(0,std::ios::end);
int nobyte = afile.tellg();
int recno = nobyte / sizeof(Student);
afile.seekg(0, std::ios::beg);
Student S;
//Loop and read every record
for(int i = 0;i<recno;i++)
{
afile.read(reinterpret_cast<char*>(&S),sizeof(S));
std::cout << "Name of Student: " << S.name << std::endl
<< "Lab mark: " << S.labTest << std::endl
<< "Assignment mark: " << S.assignments << std::endl
<< "Exam mark: " << S.exam << std::endl << std::endl;
}
afile.close();
}
int main()
{
for ( int ii= 0; ii<10; ii++) Write();
Read();
}
EDIT. Apparently, I was a bit too late in responding. Klaus has compiled a better, more comprehensive response dwelling into other problems regarding C-style char [], std::string and the endianness of the platform.
You should append to the file opened for every record. In your code you don't have this, at all. Please write the code in a way we can copy and paste, and test. As a working example, you should write some code that can be compiled and run as below:
#include <algorithm>
#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
// Student struct
struct Student {
char name[30];
float labTest;
float assignments;
float exam;
};
// Serializer
void serialize_student(const Student &s, const std::string &filename) {
// Append to the file, do not overwrite it
std::ofstream outfile(filename, std::ios::binary | std::ios::app);
if (outfile)
outfile.write(reinterpret_cast<const char *>(&s), sizeof(Student));
}
// Deserializer
std::vector<Student> deserialize_students(const std::string &filename) {
std::ifstream infile(filename, std::ios::binary);
std::vector<Student> students;
Student s;
while (infile.read(reinterpret_cast<char *>(&s), sizeof(Student)))
students.push_back(std::move(s));
return std::move(students);
}
int main(int argc, char *argv[]) {
// Generate records
std::vector<Student> mystudents;
std::generate_n(std::back_inserter(mystudents), 10, []() {
Student s;
std::strcpy(s.name, "test");
s.labTest = rand() % 100 + 1;
s.assignments = rand() % 100 + 1;
s.exam = rand() % 100 + 1;
return s;
});
// Print and write the records
for (const auto &student : mystudents) {
std::cout << student.name << ": [" << student.labTest << ','
<< student.assignments << ',' << student.exam << "].\n";
serialize_student(student, "students.bin");
}
// Read and print the records
auto records = deserialize_students("students.bin");
std::cout << "===\n";
for (const auto &student : records)
std::cout << student.name << ": [" << student.labTest << ','
<< student.assignments << ',' << student.exam << "].\n";
return 0;
}
I am new to your forum, so please forgive any missteps. I am working on a c++ project that reads and writes to a binary file. I first tried doing this using full on c++ but when an error popped up, my instructor told me to use c style file manipulation. Low and behold, I get the same error:
Unhandled exception at 0x6087CCC8 (msvcp110d.dll) in CSI_FinalProj_EmployeeDB.exe: 0xC0000005: Access violation reading location 0x00CDDAEC.
This occurs after successfully completing the read and print, and successfully closing the file. It always occurs when the program exits the function and attempts to return to the calling function. If I put it in the main, it blows up after the return, when the program ends.
The function is a simple print function:
void fileClerkType::printRecord(int id)const
{
FILE* spRead;
employeeType record;
long location;
long size;
location = id - 1;
size = sizeof(employeeType);
spRead = fopen("companyFile.dat", "r");
fseek(spRead, location*size, SEEK_SET);
fread(&record, sizeof(employeeType), 1, spRead);
// If a record has been deleted, the id will be 0
// In that case, don't print
if (record.getEmployeeID() != 0)
{
cout << record << endl;
fread(&record, sizeof(employeeType), 1, spRead);
}
fclose(spRead);
}//Unhandled exception at 0x5065CCC8 (msvcp110d.dll) in
//CSI_FinalProj_EmployeeDB.exe: 0xC0000005: Access violation
//reading location 0x00CDDAEC.
As I said, the function works perfectly. employeeType is a class that has:
2 ints, three strings, and a float
Here is the original c++ version with the same problem. The only difference is that this prints all of the records. It also works perfectly.:
void administratorType::showAllRecords()
{
long test;
long position = 0;
long recordSize = sizeof(employeeType);
ifstream inFile("EmployeesNew.dat", ios::in | ios::binary);
employeeType buffer; // empty employeeType
if(inFile.is_open())
{
inFile.seekg((position * recordSize), ios::beg);
test = inFile.peek(); // Debug
inFile.read(reinterpret_cast<char*>(&buffer), recordSize);
position = 0;
while(position < getRecordCount())
{
inFile.seekg((position * recordSize), ios::beg);
test = inFile.peek();
inFile.read(reinterpret_cast<char*>(&buffer), recordSize);
outputRecord(cout, buffer);
position++;
}
inFile.close();
}
}// Runs fine to here, but throws error when leaving the function
// Unhandled exception at 0x5408CCC8 (msvcp110d.dll) in
// ProjectName.exe: 0xC0000005: Access violation
// reading location 0x0137D3B4.
It has to be an implementation issue. But I cannot see it. Is there something in the implementation that is causing the pointers keeping track of function calls and returns to be corrupted? Thank you in advance for your help.
Sorry, here is the list of member variables for the Employee class. They are not fixed length strings:
int age;
int employeeID; // Auto-generated
float salary;
string lastName;
string firstName;
string ssn;
std::string is not a trivially copyable type, so no class that has one as a member is trivially copyable either.
You cannot read or write to non-trivially copyable types bytewise like this. The function might not crash when you read from the string due to most libraries having adopted SSO (assuming lastName, firstName, and ssn are short enough), but you'll still run into issues during destruction.
The canonical way to serialize data in c++ is to overload the stream operator, here's an example:
std::istream& operator>>(std::istream& stream, employeeType& employee)
{
return stream >>
employee.age >>
employee.employeeID >>
employee.salary >>
employee.lastName >>
employee.firstName >>
employee.ssn;
}
std::ostream& operator<<(std::ostream& stream, employeeType const& employee)
{
return stream <<
employee.age << ' ' <<
employee.employeeID << ' ' <<
employee.salary << ' ' <<
employee.lastName << ' ' <<
employee.firstName << ' ' <<
employee.ssn << '\n';
}
Records can be read or written in a loop with something like
for (employeeType e; inFile >> e;)
//do something with e
Or you can even copy them into a vector with
std::vector<employeeType> employees(
std::istream_iterator<employeeType>(inFile),
std::istream_iterator<employeeType>()
);
I created a struct to hold the data being read to the file, then converted all strings to char arrays. Doing each of those did not work, but the combination did. The following is the test program with a main() and a test class (with a struct. This is what I used to find the solution. This is a working program for those of you seeking a way to read/write binary files randomly (unless I screwed it up while formatting it in here).
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
using namespace std;
struct STUDENT
{
char lName[21];
int id;
float sal;
};
class Person
{
public:
struct STUDENT student;
string getlName() const
{
return student.lName;
}
int getID() const
{
return student.id;
}
float getSal() const
{
return student.sal;
}
// Insertion operator
friend std::ostream& operator<<(std::ostream& os, const Person& p)
{
// write out individual members of the struct with
// an end of line between each one
os << p.student.id << ' ' << p.student.lName
<< ' ' << p.student.sal << '\n';
return os;
}
// Extraction operator
friend std::istream& operator>>(std::istream& is, Person& p)
{
// read in individual members of struct
is >> p.student.id >> p.student.lName >> p.student.sal;
return is;
}
Person()
{
}
};
void outputLine( ostream&, const STUDENT&);
int main()
{
char lName[21] = {}; // Extra char for null
int id;
float sal;
int size = sizeof(STUDENT);
string more;
bool exit_now = false;
STUDENT buffer;
Person person;
// In order to randomly access data without destroying the file,
// you must use in and out (read/write mode).
fstream outFile("testFile.dat", ios::in | ios::out | ios::binary);
// Ensure file is opened
if(!outFile)
{
cerr << "Error: Out File could not be opened" << endl;
exit(1);
}
// ************* Random access inserting *************
do
{
cout << "Enter last Name\n?";
cin.getline(lName, 21);
int test;
test = strlen(lName); // FYI: this works to get char count
cout << "Enter salary\n?";
cin >> sal;
cout << "Enter ID\n?";
cin >> id;
strcpy_s(person.student.lName, lName); // copy input to struct
person.student.sal = sal;
person.student.id = id;
cout << person; // object being printed
outFile.seekp((person.student.id - 1) * size);
outFile.write(reinterpret_cast<const char* >(&person.student), size);
// Need this to get the next name
cin.clear();
cin.ignore();
cout << "Do you want to add another record? (yes or no)\n?"
<< endl;
cin >> more;
if (more == "no")
exit_now = true;
// Need this to get the next name properly
cin.clear();
cin.ignore();
}while(exit_now == false);
outFile.close();
// ************* Display Data *************
fstream inFile("testFile.dat", ios::in);
if(inFile) // Is there a connection
{
int target = 0;
int index = 0;
int position;
cout << "All records:" << endl;
while(inFile)
{
inFile.read(reinterpret_cast<char*>(&buffer), size);
if (buffer.id > 0)
{
target = inFile.tellg(); // Debug
cout << buffer.lName << endl;
}
//cout << buffer << endl; // This works
//cout << buffer.id << endl; // This works
}
cout << endl << "Search for a record by id" << endl << endl;
cout << "Enter an id: (0 to exit)" << endl;
cin >> target;
while(target > 0)
{
index = target - 1;
inFile.clear(); // Clear the flags. If the fail flags are
// are set, seekg() will not work.
// Position the file pointer
inFile.seekg(sizeof(Person)*index, ios::beg);
// Read information into the buffer (Person object)
// starting at the file pointer
inFile.read(reinterpret_cast<char*>(&buffer), size);
cout << buffer.lName << endl;
outputLine(cout, buffer);
cout << "Enter an id: (0 to exit)" << endl;
cin.clear();
cin >> target;
}
inFile.close();
cin.clear();
cin.get();
}else
cerr << endl << "Error: Could not complet the file connection."
<< "\nData could not be read."<< endl;
return 0;
}
void outputLine( ostream& output, const STUDENT& record)
{
//output << record << endl; // This works also
output << left << setw(20) << record.lName
<< setw(5) << record.id << setprecision(2)
<< right << fixed << showpoint
<< record.sal << endl;
}
I have a project for a class and I'm not sure what type of array I should be using for this program. I have to make a stock market program where the user buys, sells and views stock listings and checks their account balance. There are two text files that contain the following data:
Leon1111 5000.00
Wise2222 10000.00
Woo3333 3000.00
White4444 7000.00
Head5555 4000.00
and
Apple AAPL 450.00
Boeing BA 75.50
Intel INTC 22.30
Rambus RMBS 5.55
Sirius SIRI 3.15
Skyworks SWKS 25.35
Xilinx XLNX 36.80
This is the code I've written so far:
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <iomanip>
#include <string>
using namespace std;
int main()
{
ofstream outStream;
int option;
do
{
cout << "1) Check Stock Listings " << endl;
cout << "2) Buy Stock " << endl;
cout << "3) Sell Stock" << endl;
cout << "4) Check Account Balance " << endl;
cout << "5) Quit " << endl << endl;
cout << "Please select an option : ";
cin >> option;
cout << endl;
if (option == 1)
{
fstream CompaniesFile;
CompaniesFile.open("Companies.txt");
if (CompaniesFile.is_open())
{
string s;
while (getline(CompaniesFile, s, '\n'))
{
cout << s << endl;
}
}
CompaniesFile.close();
}
else if (option == 2)
{
}
else if (option == 3)
{
}
else if (option == 4)
{
fstream AccountFile;
AccountFile.open("Account.txt");
if (AccountFile.is_open())
{
string t;
while (getline(AccountFile, t))
{
cout << t << endl;
}
}
AccountFile.close();
}
else if (option == 5)
{
cout << "Program Terminated. Have a nice day!" << endl << endl;
}
else
{
cout << "Invalid Option entered" << endl;
}
}
while (option != 5);
return 0;
}
class cCompany
{
std::string myName;
std::string mySymbol;
double myPrice;
public:
cCompany( const std::string& name,
const std::string& symbol,
double price )
: myName( name ), mySymbol( symbol ), myPrice( price )
{}
};
std::vector< cCompany > vCompany;
class cAccount
{
std::string myName
double myBalance;
public:
cAccount( const std:string& name, double balance )
: myName( name ), myBalance( balance )
{}
};
std:vector< cAccount > vAccount;
...
std::string name;
std::string symbol;
double price;
while ( CompaniesFile.good() )
{
CompaniesFile >> name;
CompaniesFile >> symbol;
CompaniesFile >> price;
vCompany.push_back( cCompany( name, symbol, price ));
}
You are going to probably need a bit more than name and balance for an account holder, so if I had my druthers I would use a vector (or map) of a class for account holders. The account holder class would hold name, balance, and then a vector (or even better a map) of shares that the person holds and the number of shares. Something like:
class AccountHolder{
private:
std::string name_;
long long int balance_; //balance in cents
//to allow for something like buy("AAPL", 100);
// to be implemented as:
// void buy(std::string symbol, long int shares)
// {
// long int price = shares * sharePrice[symbol];
// if (balance_ >= price)
// {
// balance_ -= price;
// holdings_[symbol] += shares;
// } else
// throw(INSUFFICIENT_FUNDS);
// }
std::map<std::string, long long int> holdings_;
public:
...
};
For shares, I would use a map since you only need to know their name (and/or symbol) and the price. Maybe you can have the key be the symbol, and then the value to be price, and another map for symbol and full name. This way you can easily find the share prices: all you need to do is
std::cout << "price of a single share from " << fullName["AAPL"]
<< " is: " << sharePrice["AAPL"] << "\n";
Try to make your application more OO-like (Object Oriented). My suggestion is first you can create some data structures:
struct user { string name; double balance; }
struct stock { string name; double price; }
struct stockBought { string userName; string stockName; int share; }
then use something to save your data, for example,
list<user> users;
list<stock> stocks;
list<stockBought> stockBought;
then you should have a function to read from the two files
readUsersFromFile(users, fUsersFile);
readStocksFromFile(stocks, fStockFile);
then you should have a function to update the list
update(users, 3, 1, 4000.0); //update the users list: 3rd user, 1st column (5000.0 => 4000.0)
add(stockBought, "Leon1111", "AAPL", 1); //leon bought 1 share of AAPL
then you have to implement all the functions needed for the 5 options. Add more utility functions/classes as you move on.
Once you finish the 1st version. you can polish your code to make it run faster (adding index etc) or look better (better class).
I cannot write data on a file with these pointer variables in the class. there is no error in the program but no data is written on the file.
kindly someone tell me that where i am doing something wrong.
#include <iostream.h>
#include <fstream.h>
class studentinfo
{
private:/*Creating Private Data Members */
char* VUID;
char* campusID;
char* Studentname;
char* Fathername;
public:
void Storefile();/* Function to Store Data in the File*/
char Display();/*Function to Read and then Display Data from the File*/
studentinfo(char*, char*, char*, char*);/*Constructor to initialize Data Members*/
~studentinfo();
};
/* Constructor Defined Here*/
studentinfo::studentinfo(char* VUID, char* campusID, char* Studentname, char* Fathername)
{
cout << "Parameterized Contructor is Called" << endl << endl;
}
/*Destructor Defined Here*/
studentinfo::~studentinfo()
{
cout << "Destructor Called for destruction of the object" << endl;
system("pause");
}
/*Function to Store Data in the File Defined here*/
void studentinfo::Storefile()
{
ofstream re;
re.open("record.txt");
if(!re)/*Error Checking Mechanism*/
{
cout<<"Error Reading File"<<endl;
}
re << VUID << endl << campusID << endl << Studentname << endl << Fathername << endl;/*Using data members to Store data in the File*/
cout << "All the Data Members are Stored in a File" << endl << endl;
re.close();
}
/*Function to Read and then Display the data in the File is definde here */
char studentinfo::Display()
{
char output[100];/*Array to store and display the data*/
ifstream reh;
reh.open("record.txt");
if(!reh)
{
cout << "Error Reading File" << endl;
}
cout << "Following is My Data" << endl << endl;
while(!reh.eof()){
reh.getline(output, 100, '\n');/*Reading the data and storing it in the 'output' array line by line*/
cout << output << endl;
}
reh.close();
}
/*Main Function starting here*/
main()
{
studentinfo s1("mc130202398", "PMTN08", "Rehan Shahzad Siddiqui","Rizwan Ali Siddiqui");/*Object Created and Initialized by constructor calling*/
s1.Storefile();/*Function Call*/
s1.Display();/*Function Call*/
system("pause");
}
Your constructor is broken and leaves all the pointers unassigned. You can't use a variable's value until you assign it one.
Also, what crappy compiler are you using or what warnings settings do you have? Your constructor is being passed pointers to constants but it takes non-const pointers. That should definitely have caused a warning, pointing to your mishandling of these pointers.
studentinfo s1("mc130202398", "PMTN08", "Rehan Shahzad Siddiqui","Rizwan Ali Siddiqui");/*Object Created and Initialized by constructor calling*/
Notice you pass the constructor a bunch of constants.
studentinfo::studentinfo(char* VUID, char* campusID, char* Studentname, char* Fathername)
Oops, but the constructor takes regular char* pointers. So what are these pointers supposed to point to?
Tip: Use sensible C++ classes like std::string and these problems will magically go away.
A rewrite that addresses a number of points:
Removes the need to add using namespace std;
Uses std::string for the studentinfo member variables as per David Schwartz's recommendation
Uses a constructor initialisation list to set member variables
Replaces the unreliable eof() check
Let me know if you have any further questions.
#include <iostream>
#include <fstream>
#include <string>
class studentinfo
{
private:/*Creating Private Data Members */
std::string m_VUID;
std::string m_campusID;
std::string m_Studentname;
std::string m_Fathername;
public:
void Storefile();/* Function to Store Data in the File*/
void Display();/*Function to Read and then Display Data from the File*/
studentinfo(std::string, std::string, std::string, std::string);/*Constructor to initialize Data Members*/
~studentinfo();
};
/* Constructor Defined Here*/
studentinfo::studentinfo(std::string VUID, std::string campusID, std::string Studentname, std::string Fathername)
: m_VUID(VUID)
, m_campusID(campusID)
, m_Studentname(Studentname)
, m_Fathername(Fathername)
{
std::cout << "Parameterized Contructor is Called" << std::endl << std::endl;
}
/*Destructor Defined Here*/
studentinfo::~studentinfo()
{
std::cout << "Destructor Called for destruction of the object" << std::endl;
}
/*Function to Store Data in the File Defined here*/
void studentinfo::Storefile()
{
std::ofstream re;
re.open("record.txt");
if(!re)/*Error Checking Mechanism*/
{
std::cout << "Error opening file" << std::endl;
}
// Using data members to store data in the file
re << m_VUID.c_str() << std::endl;
re << m_campusID.c_str() << std::endl;
re << m_Studentname.c_str() << std::endl;
re << m_Fathername.c_str() << std::endl;
std::cout << "All the data members are stored in a file" << std::endl << std::endl;
re.close();
}
/* Function to read and then display the data in the file is defined here */
void studentinfo::Display()
{
std::string in;/*Array to store and display the data*/
std::ifstream reh("record.txt");
if(!reh)
{
std::cout << "Error Reading File" << std::endl;
}
std::cout << "Following is My Data" << std::endl << std::endl;
while(std::getline(reh, in))
{
std::cout << in << std::endl;
}
reh.close();
}
/* Main Function starts here*/
void main()
{
// Object created and initialised by calling constructor
studentinfo s1("mc130202398", "PMTN08", "Rehan Shahzad Siddiqui","Rizwan Ali Siddiqui");
s1.Storefile(); /*Function Call*/
s1.Display(); /*Function Call*/
system("pause");
}
I'm writing a program that uses OOP to store student records. At the moment I only have two classes, one for each individual course module called 'Courses', and one ( well two if you count the abstract base class) for the type of degree programme called 'Physics' derived from the 'Records' base class.
I'm using two maps in the program. One to store the individual courses for each individual record and sort them by course code, and one to store all the records and sort them by ID numbers.
I planned on having the user input all student information, including codes, storing this in a vector (named 'prec' in the code), then pushing the vector elements into the map used to store all the records. The code is far from finished, I was just attempting to run it to see if I was on the right track.
The code builds without any errors, but when I attempt to run it, it comes up with the error message: " Debug assertion failed: expression vector subscript out of range". I feel this may have something to do with the way I am using individual vector elements to call my functions to store courses in the maps but I cant quite get it, any help would be much appreciated!
Here are my files:
header file:
#ifndef MY_CLASS_H // Pre-processor directives to prevent multiple definition
#define MY_CLASS_h
#include <iostream>
#include <string>
#include <utility>
#include <map>
#include <fstream>
using std::string;
using std::ostream;
using std::map;
using std::cout;
using std::endl;
using std::cin;
namespace student_record // Defines the namespace student_record in which the classes are defined
{
class Course { /* Create class Course for individual courses, is this better than incorporating
all the data separately into the Record class below? Class contains course name, mark achieved and mark weight and course ID */
protected:
string course_name;
double course_mark;
int course_Id;
public:
Course() {course_name= "Null"; // Default constructor for null course
course_mark=0;
}
Course(string course_namein, double course_markin, int course_Idin) {course_name=course_namein; // Parametrized constructor to create course with set name, mark, weight and course ID
course_mark=course_markin;
course_Id=course_Idin;}
~Course() {course_name.erase(0,course_name.size());} // Destructor to delete the course name
// Access functions to get name, mark and weight //
double getmark() const {return course_mark;}
string getname() const {return course_name;}
int getid() const {return course_Id;}
friend ostream & operator << (ostream &os, const Course &c); // Friend function to overload the insertion operator for courses
};
class Record
{ // Create class Record as abstract base class for all inherited degree classes
protected:
string student_name;
int studentid;
int years;
public:
Record() {student_name="Casper";
studentid=0;
years=0;} // Default constructor for class Record, produces empty record
Record(string name, int number, int time) {student_name=name;
studentid=number;
years=time;} // Parametrized constructor for class Record
~Record() {student_name.erase(0, student_name.size());} // Destructor to delete the student name
virtual int getid()const=0;
virtual int getyears()const=0;
virtual void show_record()const=0;
virtual void print_record(string *filename)const=0;
virtual void degree_class()const=0;
virtual void insert_class()=0;
/* Virtual functions defined to be used in the derived classes (subjects ie, Physics, stamp collecting, etc...)
Thus the base class Record is abstract*/
};
class Physics: public Record
{
private:
string degree_name;
typedef map <int, Course> course_map;
course_map modules;
void searchdatabase (course_map &courses, int coursecode)const; // Uses iterator to search map for corresponding course to inputted key ( remember to move to function definitions)
string get_name (const int i, course_map &temp) const{ return temp[i].getname();}
double get_mark(const int i, course_map &temp)const{ return temp[i].getmark();} // Functions to return the mark, weight and name of a given course corresponding to inputed course code
int getid()const{return studentid;}
int getyears()const{return years;}
void show_record()const;
void print_record( string *filename) const;
void degree_class()const;
void insert_class();
// Function to insert record into map
public:
Physics():Record(){degree_name= "Physics ";}
Physics(string name,int Id, int time):Record( name, Id, time){degree_name= "Physics";}
~Physics() {degree_name.erase(0, degree_name.size());}
};
}
#endif
function definitions:
#include <iostream>
#include <string>
#include <utility>
#include <map>
#include <fstream>
#include <vector>
#include "Database_header.h"
using namespace std;
using namespace student_record;
ostream & student_record::operator<< (ostream &os, const Course &c)
{
os<< "Course code" << c.course_Id << " \n Course name: " <<c.course_name << " \n Mark " << c.course_mark <<endl;
return os;
}
// Function to insert classes //
void Physics::insert_class()
{
int courseid;
string coursename;
double mark;
cout << " Enter course code " << endl;
cin >> courseid;
cout << " \n Enter course name " << endl;
cin >> coursename;
cout << " \n Enter mark achieved " << endl;
cin >> mark;
Course temp (coursename, mark, courseid);
modules.insert(pair<int, Course>(courseid, temp));
}
void Physics::searchdatabase(course_map &courses, int coursecode) const // Function to search for specific course mark based on course code, need to modify this!!!!
//takes in a map as its argument, although i suppose can use student.modules?
{
course_map::iterator coursesIter;
coursesIter=courses.find(coursecode);
if(coursesIter != courses.end())
{
cout << " Course Code " <<
coursecode << " corresponds to " <<
coursesIter ->second << endl;
}
else { cout << " Sorry, course not found " << endl; }
}
void Physics::print_record( string *filename) const // Function for printing record to the file
{
ofstream myoutputfile;
myoutputfile.open(*filename,ios::app);
if(!myoutputfile.good())
{
// Print error message and exit
cerr<<"Error: file could not be opened"<<endl;
}
if(myoutputfile.good())
{
myoutputfile << "Student name: " << student_name << endl
<< "\n Student ID: " << studentid << endl
<< "\n Year: " << years << endl;
course_map::iterator modulesiter; // Iterator to print out courses using overloaded << function (I think?)
for(modulesiter==modules.begin();modulesiter!=modules.end();modulesiter++)
{
myoutputfile<<modulesiter->second << endl;
}
}
}
void Physics::show_record() const // Function for showing specific student record on screen ( with iterator for map of courses)
{
cout << "Student name: " << student_name;
cout << "\n Student ID: " << studentid;
cout << "\n Years on course: " << years;
cout << "\n Courses and grades: ";
course_map::iterator modulesiter; // Iterator to print out courses using overloaded << function (I think?)
for(modulesiter==modules.begin();modulesiter!=modules.end();modulesiter++)
{
cout<<modulesiter->second << endl;
}
}
void Physics::degree_class()const
{
double temp;
vector<double> dynarr; // Create a vector array to store the grades extracted from the course map for each student
course_map::iterator modulesiter;
for(modulesiter==modules.begin();modulesiter!=modules.end();modulesiter++) // Iterate through map and push values into each vector
{
Course ghost;
ghost=modulesiter->second;
dynarr.push_back(ghost.getmark());
}
double sum(0);
for(int i(0);i<=dynarr.size();i++)
{
sum+=dynarr[i];
}
temp=sum/dynarr.size();
if( temp>=40 && temp <=49.9)
{
cout << "The student has achieved a 3rd class degree with an average of: \n "
<< temp;
}
else if( temp>=50 && temp <=59.9)
{
cout << "The student has achieved a 2:2 degree with an average of: \n "
<< temp;
}
else if( temp>=60 && temp <=69.9)
{
cout << "The student has achieved a 2:1 degree with an average of: \n "
<< temp;
}
else if( temp>=70)
{
cout << "The student has achieved a 1st class degree with an average of: \n "
<< temp;
}
else { cout << "The student has failed the degree " << endl;}
}
and main cpp file:
#include <iostream>
#include <utility>
#include <map>
#include <iomanip>
#include <vector>
#include "Database_header.h"
#include <fstream>
using namespace std;
using namespace student_record;
void main()
{
// Create map to store students with ID keys //
string full_name;
int id;
int time;
string degree_name;
vector<Record*> prec;
// Vector of base class pointers to store all the different records first. No need to specify length as it is a vector! (Advantage over dynamic array?)
char student_test('y'); // Condition for adding students to the record //
int q(0);
while (student_test=='y' || student_test=='Y')
{
// Counter for while loop
cout<< " \n Please enter the student name " << endl;
getline(cin, full_name);
// Enter student name, check it is a string? //
cout<< "\n Please enter student ID " << endl;
cin >> id;
// Check if not integer or number, if not need error message //
cout << "\n Please enter the number of years on the course " << endl;
cin >> time;
// Check if not integer or number, if not need error message //
cout<< "\n Please enter degree type " << endl;
cin>>degree_name;
if(degree_name=="Physics" || degree_name=="physics") // create object of appropriate derived class ( Physics, Chem, Maths, Bio)
{
prec.push_back(new Physics(full_name, id, time));
}
char class_test('y'); // test condition for class insertion loop
while(class_test=='y') // Add courses+marks into course map
{
cout << " \n Add classes to student record " << endl;
prec[q]->insert_class();
cout << " \n Add another class? Y/N" << endl;
cin>>class_test;
}
cout << "Enter another student? Y/N " << endl;
cin >> student_test;
if(student_test=='N' && student_test=='n')
{
cout << "\n Thank you for using the student database, Goodbye !" << endl;
}
q++; // increment counter, to keep track of of vectors of base class pointers, and also be able to output number of students
}
// Next insert all records into map //
typedef map<int, Record*> studentlist;
studentlist studentmap;
for(int i(0); i<=prec.size(); i++)
{
studentmap.insert(pair<int, Record*> (prec[i]->getid(), prec[i]));
}
}
Thanks so much!
for(int i(0); i<=prec.size(); i++)
{
studentmap.insert(pair<int, Record*> (prec[i]->getid(), prec[i]));
}
Should be i < prec.size() instead of <=