I have to do the following tasks:
Read information about individual persons in file person.txt (see below) and store to array p. Set the spouse pointer for each person to NULL value first.
Perform marry operation for Mary and Tom. You can marry two people by setting their spouse pointer to point to each other (store address of one another).
Print out the content in array p where you need to print every person variable pointed by array p. If a person spouse pointer is a NULL value, then print Not Married, else print the spouse name. The output of the program is shown below. Make sure your output is the same.
I can do (1), read the text file person.txt, which has the following content:
Mary 012-35678905 20000
John 010-87630221 16000
Alice 012-90028765 9000
Tom 019-76239028 30000
Pam 017-32237609 32000
But I don't know how to do (2) and (3).
This is what I have done so far, based on the template provided with the question and that I'm not supposed to change:
#include <iostream> //>>>>>>> This part is the template given >>>>>>>
#include <cstdlib> //
#include <fstream> //
//
using namespace std; //
//
struct person //
{ //
char name[30]; //
char phone[15]; //
double money; //
person *spouse; //
}; //
//
int main() //
{ //
person *p[10]; //<<<<<<<< This is the end of the template part <<<
ifstream inFile;
inFile.open("person.txt");
if (inFile.fail())
{
cout << "Error in opening the file!" << endl;
exit(1);
}
char name[30], phone[15];
int money;
int number = 5;
for (int i = 0; i < number; i++)
{
inFile >> name >> phone >> money;
cout << "Name:" << name << endl;
cout << "Phone:" << phone << endl;
cout << "Money:" << money << endl;
cout << "Spouse Name:" << endl;
cout << endl;
}
cin.get();
system("pause");
return 0;
}
The expected output should be like this:
Name: Mary
Phone Number:012-35678905
Money: 20000
Spouse Name:Tom
Name: John
Phone Number:010-87630221
Money: 16000
Spouse Name: Not Married
...
Be aware that this exercise shows outdated use of C++
First to your array p that you somewhat forgot to use. p[10] is an array of 10. But of 10 what ? of person*, so of pointers to persons.
This is very old-fashioned C++. If you follow a course on internet, change immediately, because nowadays, we'd use vector<person>, string and nullptr. If it's a class course, you have no choice, so let's go on...
Some hints, based on what you have already done
First simplify the reading loop and don't forget to set the pointer to NULL as requested in the question:
for (int i = 0; i < number; i++)
{
person *r = new person; // allocate a new person
inFile >> r->name >> r->phone >> r->money; // read data into the new person
r->spouse = NULL; // initialize the poitner
p[i] = r; // store the pointer in the array
}
You already almost have the printing part(3). You just have to move it from your reading loop to a new loop, print from the array, and tackle the special case of married people:
for (int i = 0; i < number; i++)
{
cout << "Name:" << p[i]->name << endl;
cout << "Phone:" << p[i]->phone << endl;
cout << "Money:" << p[i]->money << endl;
cout << "Spouse:" ;
if (p[i]->spouse==NULL) {
cout << "Not married" <<endl;
}
else {
cout << p[i]->spouse->name <<endl;
}
cout << endl;
}
Now something to do on your own
Now about marrying Marry and Tom in (2). This is more delicate. I will not do it for you, because now you have all you need to finish homework. But the general principle is:
Create two pointers spouse1 and spouse2 and initialize them to NULL.
Loop through the array to find which person is Tom and which one is Marry, and update the relevant pointer (e.g. spouse1 = p[i]; )
At the end of the loop, check that we have found both spouses (both pointers are not NULL anymore, and both pointers are different, because you cannot marry someone with hi/her-self)
If it's ok, then just marry them: spouse1->spouse=spouse2; spouse2->spouse=spouse1;
Finally, before you end the programme, you need to deallocate all the pointers in the array (with vectors, you wouldn't have to care about this).
Further improvements needed
You still need to improve your reading loop, to make it more dynamic. Because in reality, you don't know how many lines are in the text file. So start with number=0 and read data as long as possible, incrementing number each time, but stoping if not possible to read anymore, or if the maximum size of the array is reached.
Related
I made a simple array of struct I made a function to implement the array from users input
but I am struggling to find the right way to free or delete elements in the array ;
here is my code for a better understanding
#include <iostream>
using namespace std;
typedef struct InfStudent
{
int id;
int age;
int lvel;
}studentInfo;
void addElments(studentInfo *s)
{
int i=0;
for(i=0; i<2; i++) {
s[i].id = i;
s[i].age = i * i + 1;
s[i].lvel = i + i + 2;
}
}
int studCounter = 0 ;
void deltetElement(void *studentInfo1 ) {
for (int i = 0; i < 6; i++) {
cout << "empty " << endl;
free(studentInfo1);
}
}
int main()
{
int n;int i;
studentInfo st[2];
addElments(st);
for(i=0; i<2; i++) {
cout<<"enter the Id number of the student "<< endl;
cin >> st[i].id;
cout<<"enter the age of the student "<< endl;
cin >> st[i].age;
cout<<"enter the level of the student "<< endl;
cin >> st[i].lvel;
}
deltetElement(st);
for(i=0; i<2; i++) {
cout << "Id of the student " << i << "\t=" << st[i].id;
cout << "\t Age of the student " << i << "\t=" << st[i].age;
cout << "\tLevel of the student " << i << "\t=" << st[i].lvel;
cout<< endl;
}
return 0;
}
the output
enter the Id number of the student
1234
enter the age of the student
32
enter the level of the student
2
enter the Id number of the student
321
enter the age of the student
2
enter the level of the student
32
empty
it is printing empty, but the code still working like 3 second and then printing empty massage, but I did not understand it does delete or not, or there is a better way to do that;
Since you are using studentInfo st[2]; it's an array of VALUE types, meaning that studentInfo objects are not allocated in heap, and should not be deleted by free() or delete.
free or delete array of strcut c++
You create an array with automatic storage. Automatic objects are destroyed and their storage is released automatically when the variable goes out of scope. You cannot and you must not "free" them in any way other than by letting the execution proceed to the outside of the scope where the automatic object is defined.
Only thing that may be passed to free is a pointer that was returned by malloc (or certain other related C allocation functions) and hasn't previously been freed. Since that doesn't apply to what you pass to free, the behaviour of your program is undefined. That's bad. Don't do that.
P.S. Don't use malloc nor free in C++ if you can avoid it (and it can usually be avoided).
I am struggling to find the right way to free or delete elements in the array ;
The elements of an array are destroyed and their storage released when the array itself is destroyed and its memory is released. There is no way to separate those two.
I have a dynamically resizing struct[]. I ask the user for how many records he wants then make that many struct.Then I store the name and age info in the struct. The problem is when printing out that data i am only printing the last name and age the user input. I would like to print all the values.
struct Records
{
char name [32] ;
int age;
};
void program2()
{
Records rec;
int size;
cout << "How many record would you like?";
cin >> size;
cout << "\n" << size;
Records* a = new Records[size];
for (int i = 0; i < size; i++)
{
cout << "Whats the name for the Record: ";
cin >> rec.name;
cout << rec.name;
cout << "What is the age for this record: ";
cin >> rec.age;
}
for (int i = 0; i < size; i++)
{
cout << "\n" << rec.name << rec.age;
}
}
In your code, the variable rec has nothing to do with the array. To access the element at position i of your array, you'll need to use a[i].
For example:
cin >> a[i].name;
or
cout << "\n" << a[i].name << " " << a[i].age;
No surprise - you are actually printing repeatedly the last record that you created (rec). Your last loop is not actually traversing the array.
Also, the first loop only creates a struct, but does not actually add it to the array.
What you are trying to do is actually easier and cleaner if you use vectors instead of arrays, adding each record to the vector using push_back(). This is the closest thing to the "dynamically resized array" that you are referring to (such a thing does not actually exist in C++, that's what vectors are for, amongst other things).
Have a look into doing it this way, and if you get stuck feel free to ask again, happy to help.
Current code:
const int MAX_CODENAME = 25;
const int MAX_SPOTS = 5;
struct Team {
string TeamName[MAX_CODENAME];
short int totalLeagueGames;
short int leagueWins;
short int leagueLoses;
};
//GLOBAL VARIABLES:
Team league[MAX_SPOTS];
void addTeams(){
int i = 0; //first loop
int j; //second loop
while(i < MAX_SPOTS){
cout << "****** ADD TEAMS ******" << endl;
cout << "Enter the teams name " << endl;
scanf("%s", league[i].TeamName) ;
}
void searchTeam(){
string decider[MAX_CODENAME];
cout << "Please enter the team name you would like the program to retrieve: " << endl;
cin >> decider[MAX_CODENAME];
for(int i = 0; i < MAX_SPOTS; i++){
if(decider == league[i].TeamName){
cout << endl;
cout << league[i].TeamName << endl;
break;
}else{
cout << "Searching...." << endl;
}
}
}
I really dont know why its not working but I have included all the perquisite header files such as and but the program crashes when i enter the data and then attempt to search. I get the circle of death and then program not responding then says Process returned 255 (0xFF) . It does not even out put Searching.... the program practically gives up as soon as I enter that name.
Also if this can be optimized by the use of pointers that would be great.
tl;dr run-time error causing the search to fail as soon as i type in a name. And for the record I have checked to make sure the name I entered is valid.
scanf doesn't know about std::string. Use std::cin >> league[i].TeamName.
scanf("%s", league[i].TeamName) ;
This should be changed to
std::cin >> league[i].TeamName ;
A couple of other things here....
string decider[MAX_CODENAME];
cout << "Please enter the team name you would like the program to retrieve: " << endl;
cin >> decider[MAX_CODENAME];
Every time you input a value, you are telling the computer to hold the inputted value at decider[25] but the computer only reads indexes 0-24.
if(decider == league[i].TeamName){
Which array slot are you comparing the team name to? If its the 25th element than the statement should be
if(decider[24] == league[i].TeamName){
Pointers are better suited if the number of TeamNames are unknown. Based on the limited code presented, I highly recommend you stay within the realm of basic data types. For the purposes of troubleshooting, please post your full code in the future.
Your TeamName member variable:
string TeamName[MAX_CODENAME];
is an array of 25 strings, so in this line:
scanf("%s", league[i].TeamName) ;
you are courrupting the array. You don't really want an array anyways, so change the TeamName declaration to:
string TeamName;
and then when you read the name, you'll need to use iostreams which knows how to populate a string type (scanf only works with c char arrays):
std::cin >> league[i].TeamName
#include<fstream>
#include<iostream>
using namespace std;
class employee
{
char *name;
int age;
string designation; // string data type is used
float salary;
public:
void getdata();
void display();
};
void employee::getdata() // for taking the input
{
cout<<"\nENTER THE NAME OF THE EMPLOYEE: ";
gets(name); /*name is a char pointer */
cout<<"\nENTER THE AGE OF THE EMPLOYEE: ";
cin>>age;
cout<<"\nENTER THE DESIGNATION OF THE EMPLOYEE: ";
getline(cin,designation); /*designation is string data type*/
cout<<"\nENTER THE SALARY OF THE EMPLOYEE: ";
cin>>salary;
}
void employee::display()//for displaying the inputed data
{
cout<<"\nTHE NAME OF THE EMPLOYEE: ";
puts(name);
cout<<"\nENTER THE AGE OF THE EMPLOYEE: ";
cout<<age;
cout<<"\nENTER THE DESIGNATION OF THE EMPLOYEE: ";
cout<<designation;
cout<<"ENTER THE SALARY OF THE EMPLOYEE: ";
cout<<salary;
}
int main()
{
ofstream fout;
char ch;
fout.open("employee.txt",ios::out|ios::binary);/*use of open function in file handing*/
employee e;
for(int i=0;i<3;i++)
{
e.getdata();
fout.write((char*)&e,sizeof(e));//write() is used
}
fout.close();
ifstream fin;
fin.open("employee.txt",ios::in|ios::binary);
int j;
cout<<"\n Enter the employee no. to be read ";
cin>>j;
fin.seekg((j-1)*sizeof(e));
fin.read((char*)&e,sizeof(e));
cout<<"\n Details of employee no. of object is = ";
e.display();
return 0;
}
I am not able to identify the error in my code...I had cross checked the code...there is no syntax error and no compiler error but the output is not correct...it is not taking the proper input from the user.
As Bo Persson said, your program does not provide any memory for name and designation. You declare pointers like name in your class alright, but they do not point to anything. You could just declare e.g. name as char name[100]; and hope that 100 is enough (and, in production code, add checks to ensure it isn't exceeded).
But in C++ there is the string class which frees you of many worries. In particular, it makes input of arbitrarily long strings easy. If the string can't have whitespace in it, a simple string s; cin >> s; reads a "word" from the input into the string. If it can have whitespace, you need some way to tell where it starts and ends. Databases, Excel etc. often use quotes to surround a string. If it cannot contain newlines, ensuring that it is on a line of its own is sufficient and obviates the need for parsing. That's what we'll do here.
The second issue you faced is what's technically called "serialization". You want to store ("persist") your class on disk and later (much later, perhaps) re-read it into a new instance. Nathan Oliver pointed you to a resource which discusses serialization. For a simple class like employee with a limited number of simple data members we can simply roll our own ad-hoc serialization: We write everything to disk with operator<<(), and read everything back with operator>>().
The main thing to consider is that strings may have whitespace, so that we'll put them on a line of their own.
An addition is that in order to be more robust when reading from a file we start each employee with a start marker (header in the code below). This way reading an employee will work from any position in the file. Also, if later employees should have more fields we can still read our basic employee data and just skip the additional fields before we read the next employee in a sequence of employees on disk.
streams are closed automatically when they are destroyed at the end of their scope; we use block scope for that (check the additional {} in the code).
A plain float is not precise enough for higher salaries (it only has about 7 decimal digits, so that for salaries > 167772.16 (if I can believe Wikipedia) in whatever currency the pennies start to fall off the cliff). I use long double and make sure to not lose precision when converting it to text.
You didn't compare reals but I did in order to check whether I read the employee back correctly. Care must be taken there. I wiggle out of the gory details by comparing half pennies which should be appropriate for money.
Here is the whole program. (Compared with my previous version I have simplified the (de)serialization, in particular I have eliminated the useless tags.)
It would be wise to perform error checking after each read/write in order to make sure it succeeded; remember, a stream coverts to bool, so a simple if(!os) { cerr << "oops" << endl; /* exit? */} is enough; but I didn't want to distract from the actual program too much.
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <cfloat> // for LDBL_DIG in ostream precision.
#include <cstdlib> // for exit()
using namespace std;
/** A simple class holding employee information */
class employee
{
public:
/** This header starts each
"serialization" of an employee on a line
of its own. */
static constexpr const char *header = "--- Employee ---";
// use C++ std::string
string name;
int age;
// use C++ std::string
string designation;
// Be as precise as possible,
// for later uses like salary increases
// by 4.5% or so :-)
// The salary is in units like USD or EUR.
// The fraction part is pennies, and fractions of them.
long double salary;
public:
void readdata(istream &is);
void writedata(ostream &os);
void display();
bool operator==(employee &rhs)
{ return
name == rhs.name
&& age == rhs.age
&& designation == rhs.designation
// Do not compare floats directly.
// We compare pannies, leaving slack for rounding.
// If two salaries round to the same penny value,
// i.e. 0.01, they are equal for us.
// (This may not be correct, for an accounting app,
// but will do here.)
&& salary - rhs.salary < 0.005
&& rhs.salary - salary < 0.005;
}
};
/** Write a header and then
each data member in declaration order, converted to text,
to the given stream. The header is used to find the start
of the next employee; that way we can have comments or other
information in the file between employees.
The conversion is left to operator<<.
Each member is written to a line of its own, so that we
can store whitespace in them if applicable.
The result is intended to be readable by #readdata().
*/
void employee::writedata(ostream &os)
{
os.precision(LDBL_DIG); // do not round the long double when printing
// make sure to start on a new line....
os << endl
// ... write the header on a single line ...
<< header << endl
// ... and then the data members.
<< name << endl
<< age << endl
<< designation << endl
<< salary << endl;
}
/**
Read an amployee back which was written with #writedata().
We first skip lines until we hit a header line,
because that's how an employee record starts.
Then we read normal data mambers with operator>>.
(Strictly spoken, they do not have to be on lines
of thier own.)
Strings are always on a line of their own,
so we remove a newline first.
*/
void employee::readdata(istream &is)
{
string buf;
while(getline(is, buf)) // stream converts to bool; true is "ok"
{
if( buf == header) break; // wait for start of employee
}
if( buf != header )
{
cerr << "Error: Didn't find employee" << endl;
return;
}
getline(is, name); // eats newline, too
is >> age; // does not eat newline:
// therefore skip all up to and including the next newline
is.ignore(1000, '\n');
// line on its own, skips newline
getline(is, designation);
is >> salary;
}
int main()
{
const char *const fname = "emp.txt";
employee empa;
empa.name = "Peter A. Schneider";
empa.age = 42;
empa.designation = "Bicycle Repair Man";
empa.salary = 12345.67;
employee empb;
empb.name = "Peter B. Schneider";
empb.age = 43;
empb.designation = "Bicycle Repair Woman";
empb.salary = 123456.78;
{
ofstream os(fname);
if(!os)
{
cerr << "Couldn't open "
<< fname << " for writing, aborting" << endl;
exit(1);
}
empa.writedata(os);
cout << "Employee dump:" << endl;
empa.writedata(cout);
// insert a few funny non-employee lines
os << endl << "djasdlköjsdj" << endl << endl;
empb.writedata(os);
cout << "Employee dump:" << endl;
empb.writedata(cout);
}
// show the file contents
{
ifstream is(fname);
if(!is)
{
cerr << "Couldn't open "
<< fname << " for reading, aborting" << endl;
exit(1);
}
string line;
cout << "-------------- File: -------------" << endl;
while(getline(is, line)) cout << line << endl;
cout << "---------------End file ----------" << endl;
}
/////////////////////////////////////////////////////////
{
ifstream is(fname); // read from the file "emp.txt" just written
if(!is)
{
cerr << "Couldn't open "
<< fname << " for reading, aborting" << endl;
exit(1);
}
{
employee emp2; // new employee, sure to be empty
cout << endl << "Re-reading an employee..." << endl;
emp2.readdata(is);
cout << endl << "Re-read employee dump:" << endl;
emp2.writedata(cout);
cout << "Original and written/read employee are "
<< (empa == emp2 ? "" : "NOT ") << "equal" << endl;
}
{
employee emp2; // new employee, sure to be empty
// now read next employee from same stream.
// readdata() should skip garbage until the header is found.
cout << endl << "Re-reading an employee..." << endl;
emp2.readdata(is);
cout << endl << "Re-read employee dump:" << endl;
emp2.writedata(cout);
cout << "Original and written/read employee are "
<< (empb == emp2 ? "" : "NOT ") << "equal" << endl;
}
}
}
Sample session:
Employee dump:
--- Employee ---
Peter A. Schneider
42
Bicycle Repair Man
12345.6700000000001
Employee dump:
--- Employee ---
Peter B. Schneider
43
Bicycle Repair Woman
123456.779999999999
-------------- File: -------------
--- Employee ---
Peter A. Schneider
42
Bicycle Repair Man
12345.6700000000001
djasdlköjsdj
--- Employee ---
Peter B. Schneider
43
Bicycle Repair Woman
123456.779999999999
---------------End file ----------
Re-reading an employee...
Re-read employee dump:
--- Employee ---
Peter A. Schneider
42
Bicycle Repair Man
12345.6700000000001
Original and written/read employee are equal
Re-reading an employee...
Re-read employee dump:
--- Employee ---
Peter B. Schneider
43
Bicycle Repair Woman
123456.779999999999
Original and written/read employee are equal
In addition to what the comments say about gets being dangerous, you immediately run into that buffer overflow by not allocating any space for name. Just having a pointer is not enough.
In addition to that, mixing cin >> and getline(cin,...) is known to skip input, because the two functions handle end-of-line differently.
Then we have the problem of doing binary I/O for the employee type. In general, you cannot do that for class types that are non-trivial. Specifically, the std::string designation member internally holds a pointer to some data. That pointer will not survive the transfer to disk and back.
One of the exercises in C++ Primer Plus is having me use fstream to open a txt file and input data into a structure then output it. First line of the txt file is the number of "donors". The problem I seem to be having is that (I think) when I use "inFile >> value;" to retrieve the number and then allocate the structure via new, its expecting an int and its getting a string? Is this correct? What should I be doing differently?
//ch6 p278 exercise #9
#include <iostream>
#include <cstring>
#include <fstream>
#include <cstdlib>
using namespace std;
const int SIZE = 60;
struct contributions
{
char name[20];
double dollars;
};
char filename[20];
string donors;
bool donated;
int main()
{
char filename[SIZE];
cout << "Enter name of file to scan: ";
cin >> filename;
fstream inFile;
inFile.open(filename);
if(!inFile.is_open())
{
cout << "Could not open the file " << filename << endl;
cout << "Program terminating.\n";
exit(EXIT_FAILURE);
}
inFile >> donors;
contributions * ptr = new contributions[donors];
for(int h = 0; h < donors; h++)
{
inFile >> ptr[h].name);
inFile >> ptr[h].dollars);
}
//BEGIN OUTPUT OF STRUCTURES
cout << "GRAND PATRONS:\n";
for(int i = 0; i < donors; i++)
{
if(ptr[i].dollars >= 10000)
{
cout << ptr[i].name << " donated " << ptr[i].dollars << endl;
donated = true;
}
}
if (!donated) {cout << "none.\n";}
donated = false;
cout << "PATRONS:\n";
for(int i=0; i < donors; i++)
{
if(ptr[i].dollars < 10000)
{
cout << ptr[i].name << " donated " << ptr[i].dollars << endl;
donated = true;
}
}
if (!donated) {cout << "none.\n";}
delete ptr;
return 0;
}
What should I be doing differently?
Well, if the code is based on what C++ Primer Plus has taught you - use a different text book. I don't want to insult you, but the code is really poor. For example, has the book covered the std::string class?
I would recommend junking it, and getting Accelerated C++ by Koenig & Moo (two of the original C++ team) which will teach you modern, good, idiomatic C++.
You are most certainly correct, I assume the code does not compile? You can just change the donors variable to an integer. The input streams are overloaded to work with all built in types, ie. bool, double, int etc.
The following will work, assuming the next value is an integer:
int numDonors;
inFile >> numDonors;
Note that the change to an integer is also necessary for the 'for' loops to work correctly, you are currently comparing a string to an integer, there is no built-in/default behavior for this.
Edit: I also noticed that when you delete your array of contributors you do:
delete ptr;
However, this will only clear out the first dynamically allocated block. The correct way to delete an array of dynamically allocated objects is:
delete [] ptr;
Edit 2: Thanks for the sample, so the reason it is not working correctly is the fact that the names are first and last, and the stream operator '>>' breaks on whitespace. So what is essentially happening is you first attempt to read the name, the stream reads the first name only and stops. Then you try to read the donated value, this attempts to read the last name as the value and cannot convert to an double so it returns 0.
donors is a string, and you are trying to use it as an array size:
string donors;
//...
inFile >> donors;
contributions * ptr = new contributions[donors];
You should make donors an int. Also it's usually better to use std::vector<> instead of manually messing with pointers to raw arrays. That would look something like this:
std::vector<contributions> contribs;
for (...) {
contributions contrib;
// .. Some way read contrib from inFile
contribs.push_back(contrib);
}