I have to develop a C++ program that reads the student’s name and scores from a file and store them in an array of Student struct objects, then calculate and display each student’s final grade based on the following criteria: midterm exam is counted for 25% of the final grade, final exam is counted for 25% of the final grade and average of 4 labs is counted for 50% of the final grade.
But my knowledge is “limited” and more when it comes to using structures so there are things that I'm not sure how to do or implement things(look at the comments in the program below, basically with that I need help). I've tried a few methods but so far none have worked properly. And unfortunately, I can't change much of the "template" that they’ve provided me(They've provided me with the variables, or rather some of them, letterGrade, Student students[24], newStudent, etc..)... so that has made things a bit more complicated for me... I'm not one of those who usually ask for help. In fact, I would like to solve it myself, but I don't have much time... I'd appreciate some pointers or something, thank you.
The format of the file is ---
// student name
// midterm exam scores, final exam scores
// lab1 score, lab2 score, lab3 score, lab4 score
e.g.,
Joe Doe
90.8 89.5
67 89.2 99.0 100.0
Susan F. Smith
95.5 94.0
78.5 90 87.5 57
Sam Grover
78 100.0
79.5 69.4 90.0 88.5
Here is what I've done so far:
#include <iostream>
#include <cstdlib>
#include <string>
#include <fstream>
using namespace std;
/* structure */
struct Student {
string name;
double midTerm;
double finalExam;
double lab1;
double lab2;
double lab3;
double lab4;
};
/* function prototype */
double calculateGrade(Student s);
/* main function */
int
main(void)
{
Student newStudent;
Student students[24];
ifstream inFile;
int numStudent = 0;
char letterGrade = ' ';
inFile.open("students.txt");
if (!inFile)
cerr << "Unable to open file.\n";
// read the first student’s record, write some code here
double pct;
while (inFile) {
// call calculateGrade function to calculate student’s final numeric score
// and update student’s record, write some code here
pct = calculateGrade(newStudent);
// store the current student record in the students array and update numStudent
// write some code here
// ignore the ‘\n’ at the end of current student record in the data file
// before reading next student record, write your code here
// read next student record, write some code here
numStudent++;
}
for (int i = 0; i < numStudent; i++) {
if (pct >= 90.0)
letterGrade = 'A';
else if (pct <= 89.9 && pct >= 80.0)
letterGrade = 'B';
else if (pct <= 79.9 && pct >= 70.0)
letterGrade = 'C';
else if (pct <= 69.9 && pct >= 60.0)
letterGrade = 'D';
else
letterGrade = 'F';
cout << students[i].name << "'s final grade is " << pct
<< "% (grade: " << letterGrade << ")" << endl;
}
exit(EXIT_SUCCESS);
}
double
calculateGrade(Student s)
{
double exams, labs;
exams = 0.25 * (s.midTerm) + 0.25 * (s.finalExam);
labs = 0.125 * (s.lab1 + s.lab2 + s.lab3 + s.lab4);
return (exams + labs);
}
An example of the output would be:
David Smith’s final grade is 85.42% (grade: B)
Ian Davis's final grade is 97.34% (grade: A)
...
You should use the C++ goodies, and learn to be more conservative with the idioms.
main
should be int main()
or int main(int argc, char *argv[])
. On some environments you can also use int main(int argc, char *argv[], char **environ)
but never use the ugly void main()
. Despite being equivalent in C++ int main(void)
will only unsettle future readers.
In C++ a class (or a struct because it is the same thing) can contain methods. Instead of building a free function using a single argument that is an instance of a class it is generally better to make a method from it.
When you detect an error condition and write a fatal error message, you should not continue the program flow.
The injectors (resp. extractors) can be used to directly extract (resp. write) an object from (rest. into) a stream. It generally gives a more idiomatic code.
As you said that Student students[24]
was provided I kept it, but in fact a single Student could be because you could print at read time. Here I have separated the reading of the records from the prints.
SO here is a possible code:
#include <iostream>
#include <string>
#include <fstream>
// avoid using namespace std because it really import too many symbols
using std::string;
using std::ifstream;
using std::istream;
using std::cerr;
using std::cout;
using std::endl;
/* structure */
struct Student {
string name;
double midTerm;
double finalExam;
double lab[4]; // making lab an array allows iterating on it
// you can directly "cache" the computed values in the object itself
double grade;
string letterGrade;
/* function prototype - better to make it a method */
void calculateGrade();
};
// an extractor for the Student class
istream& operator >> (istream& in, Student& student) {
if (!std::getline(in, student.name)) return in; // immediately give up on eof
in >> student.midTerm >> student.finalExam;
for (double& lab : student.lab) {
in >> lab;
}
if (in) {
student.calculateGrade();
// skip the end of last line because we want to use getline...
if (in.ignore().eof()) { // but do not choke on end of file
in.clear();
}
}
return in;
}
/* main function */
int
main()
{
Student newStudent;
Student students[24];
ifstream inFile;
unsigned nStudents = 0;
inFile.open("students.txt");
if (!inFile) {
cerr << "Unable to open file.\n";
return EXIT_FAILURE;
}
// C++ allows to directly iterate any container including a plain array
for (Student& student : students) {
inFile >> student; // as we iterate through references we can change the object
if (inFile) {
nStudents++;
}
else { // give up on error or eof
if (!inFile.eof()) { // but only write a message on error
cerr << "Error reading file.\n";
}
break;
}
}
// time to display the results
for (int i = 0; i < nStudents; i++) {
cout << students[i].name << "'s final grade is " << students[i].grade
<< "% (grade: " << students[i].letterGrade << ")" << endl;
}
exit(EXIT_SUCCESS);
}
// the method implementation
void
Student::calculateGrade()
{
double exams, labs;
exams = 0.25 * (midTerm) + 0.25 * (finalExam);
labs = 0.125 * (lab[0] + lab[1] + lab[2] + lab[3]);
grade = (exams + labs);
if (grade >= 90.0)
letterGrade = 'A';
else if (grade <= 89.9 && grade >= 80.0)
letterGrade = 'B';
else if (grade <= 79.9 && grade >= 70.0)
letterGrade = 'C';
else if (grade <= 69.9 && grade >= 60.0)
letterGrade = 'D';
else
letterGrade = 'F';
}