Case Study: Class GradeBook Using an Array to Store Grades
Case Study Class GradeBook Using an Array to Store Grades
This section further evolves class GradeBook, introduced in Chapter 3 and expanded in Chapters 46. Recall that this class represents a grade book used by a professor to store and analyze a set of student grades. Previous versions of the class process a set of grades entered by the user, but do not maintain the individual grade values in data members of the class. Thus, repeat calculations require the user to reenter the same grades. One way to solve this problem would be to store each grade entered in an individual data member of the class. For example, we could create data members grade1, grade2, ..., grade10 in class GradeBook to store 10 student grades. However, the code to total the grades and determine the class average would be cumbersome. In this section, we solve this problem by storing grades in an array.
Storing Student Grades in an Array in Class GradeBook
The version of class GradeBook (Figs. 7.167.17) presented here uses an array of integers to store the grades of several students on a single exam. This eliminates the need to repeatedly input the same set of grades. Array grades is declared as a data member in line 29 of Fig. 7.16therefore, each GradeBook object maintains its own set of grades.
Figure 7.16. Definition of class GradeBook using an array to store test grades.
(This item is displayed on pages 351 - 352 in the print version)
1 // Fig. 7.16: GradeBook.h 2 // Definition of class GradeBook that uses an array to store test grades. 3 // Member functions are defined in GradeBook.cpp 4 5 #include // program uses C++ Standard Library string class 6 using std::string; 7 8 // GradeBook class definition 9 class GradeBook 10 { 11 public: 12 // constant -- number of students who took the test 13 const static int students = 10; // note public data 14 15 // constructor initializes course name and array of grades 16 GradeBook( string, const int [] ); 17 18 void setCourseName( string ); // function to set the course name 19 string getCourseName(); // function to retrieve the course name 20 void displayMessage(); // display a welcome message 21 void processGrades(); // perform various operations on the grade data 22 int getMinimum(); // find the minimum grade for the test 23 int getMaximum(); // find the maximum grade for the test 24 double getAverage(); // determine the average grade for the test 25 void outputBarChart(); // output bar chart of grade distribution 26 void outputGrades(); // output the contents of the grades array 27 private: 28 string courseName; // course name for this grade book 29 int grades[ students ]; // array of student grades 30 }; // end class GradeBook |
Note that the size of the array in line 29 of Fig. 7.16 is specified by public const static data member students (declared in line 13). This data member is public so that it is accessible to the clients of the class. We will soon see an example of a client program using this constant. Declaring students with the const qualifier indicates that this data member is constantits value cannot be changed after being initialized. Keyword static in this variable declaration indicates that the data member is shared by all objects of the classall GradeBook objects store grades for the same number of students. Recall from Section 3.6 that when each object of a class maintains its own copy of an attribute, the variable that represents the attribute is also known as a data membereach object (instance) of the class has a separate copy of the variable in memory. There are variables for which each object of a class does not have a separate copy. That is the case with static data members, which are also known as class variables. When objects of a class containing static data members are created, all the objects of that class share one copy of the class's static data members. A static data member can be accessed within the class definition and the member-function definitions just like any other data member. As you will soon see, a public static data member can also be accessed outside of the class, even when no objects of the class exist, using the class name followed by the binary scope resolution operator (::) and the name of the data member. You will learn more about static data members in Chapter 10.
The class's constructor (declared in line 16 of Fig. 7.16 and defined in lines 1724 of Fig. 7.17) has two parametersthe name of the course and an array of grades. When a program creates a GradeBook object (e.g., line 13 of fig07_18.cpp), the program passes an existing int array to the constructor, which copies the values in the passed array to the data member grades (lines 2223 of Fig. 7.17). The grade values in the passed array could have been input from a user or read from a file on disk (as discussed in Chapter 17, File Processing). In our test program, we simply initialize an array with a set of grade values (Fig. 7.18, lines 1011). Once the grades are stored in data member grades of class GradeBook, all the class's member functions can access the grades array as needed to perform various calculations.
Figure 7.17. GradeBook class member functions manipulating an array of grades.
(This item is displayed on pages 352 - 355 in the print version)
1 // Fig. 7.17: GradeBook.cpp 2 // Member-function definitions for class GradeBook that 3 // uses an array to store test grades. 4 #include 5 using std::cout; 6 using std::cin; 7 using std::endl; 8 using std::fixed; 9 10 #include 11 using std::setprecision; 12 using std::setw; 13 14 #include "GradeBook.h" // GradeBook class definition 15 16 // constructor initializes courseName and grades array 17 GradeBook::GradeBook( string name, const int gradesArray[] ) 18 { 19 setCourseName( name ); // initialize courseName 20 21 // copy grades from gradeArray to grades data member 22 for ( int grade = 0; grade < students; grade++ ) 23 grades[ grade ] = gradesArray[ grade ]; 24 } // end GradeBook constructor 25 26 // function to set the course name 27 void GradeBook::setCourseName( string name ) 28 { 29 courseName = name; // store the course name 30 } // end function setCourseName 31 32 // function to retrieve the course name 33 string GradeBook::getCourseName() 34 { 35 return courseName; 36 } // end function getCourseName 37 38 // display a welcome message to the GradeBook user 39 void GradeBook::displayMessage() 40 { 41 // this statement calls getCourseName to get the 42 // name of the course this GradeBook represents 43 cout << "Welcome to the grade book for " << getCourseName() << "!" 44 << endl; 45 } // end function displayMessage 46 47 // perform various operations on the data 48 void GradeBook::processGrades() 49 { 50 // output grades array 51 outputGrades(); 52 53 // call function getAverage to calculate the average grade 54 cout << " Class average is " << setprecision( 2 ) << fixed << 55 getAverage() << endl; 56 57 // call functions getMinimum and getMaximum 58 cout << "Lowest grade is " << getMinimum() << " Highest grade is " 59 << getMaximum() << endl; 60 61 // call function outputBarChart to print grade distribution chart 62 outputBarChart(); 63 } // end function processGrades 64 65 // find minimum grade 66 int GradeBook::getMinimum() 67 { 68 int lowGrade = 100; // assume lowest grade is 100 69 70 // loop through grades array 71 for ( int grade = 0; grade < students; grade++ ) 72 { 73 // if current grade lower than lowGrade, assign it to lowGrade 74 if ( grades[ grade ] < lowGrade ) 75 lowGrade = grades[ grade ]; // new lowest grade 76 } // end for 77 78 return lowGrade; // return lowest grade 79 } // end function getMinimum 80 81 // find maximum grade 82 int GradeBook::getMaximum() 83 { 84 int highGrade = 0; // assume highest grade is 0 85 86 // loop through grades array 87 for ( int grade = 0; grade < students; grade++ ) 88 { 89 // if current grade higher than highGrade, assign it to highGrade 90 if ( grades[ grade ] > highGrade ) 91 highGrade = grades[ grade ]; // new highest grade 92 } // end for 93 94 return highGrade; // return highest grade 95 } // end function getMaximum 96 97 // determine average grade for test 98 double GradeBook::getAverage() 99 { 100 int total = 0; // initialize total 101 102 // sum grades in array 103 for ( int grade = 0; grade < students; grade++ ) 104 total += grades[ grade ]; 105 106 // return average of grades 107 return static_cast< double >( total ) / students; 108 } // end function getAverage 109 110 // output bar chart displaying grade distribution 111 void GradeBook::outputBarChart() 112 { 113 cout << " Grade distribution:" << endl; 114 115 // stores frequency of grades in each range of 10 grades 116 const int frequencySize = 11; 117 int frequency[ frequencySize ] = { 0 }; 118 119 // for each grade, increment the appropriate frequency 120 for ( int grade = 0; grade < students; grade++ ) 121 frequency[ grades[ grade ] / 10 ]++; 122 123 // for each grade frequency, print bar in chart 124 for ( int count = 0; count < frequencySize; count++ ) 125 { 126 // output bar labels ("0-9:", ..., "90-99:", "100:" ) 127 if ( count == 0 ) 128 cout << " 0-9: "; 129 else if ( count == 10 ) 130 cout << " 100: "; 131 else 132 cout << count * 10 << "-" << ( count * 10 ) + 9 << ": "; 133 134 // print bar of asterisks 135 for ( int stars = 0; stars < frequency[ count ]; stars++ ) 136 cout << '*'; 137 138 cout << endl; // start a new line of output 139 } // end outer for 140 } // end function outputBarChart 141 142 // output the contents of the grades array 143 void GradeBook::outputGrades() 144 { 145 cout << " The grades are: "; 146 147 // output each student's grade 148 for ( int student = 0; student < students; student++ ) 149 cout << "Student " << setw( 2 ) << student + 1 << ": " << setw( 3 ) 150 << grades[ student ] << endl; 151 } // end function outputGrades |
Figure 7.18. Creates a GradeBook object using an array of grades, then invokes member function processGrades to analyze them.
1 // Fig. 7.18: fig07_18.cpp 2 // Creates GradeBook object using an array of grades. 3 4 #include "GradeBook.h" // GradeBook class definition 5 6 // function main begins program execution 7 int main() 8 { 9 // array of student grades 10 int gradesArray[ GradeBook::students ] = 11 { 87, 68, 94, 100, 83, 78, 85, 91, 76, 87 }; 12 13 GradeBook myGradeBook( 14 "CS101 Introduction to C++ Programming", gradesArray ); 15 myGradeBook.displayMessage(); 16 myGradeBook.processGrades(); 17 return 0; 18 } // end main
|
Member function processGrades (declared in line 21 of Fig. 7.16 and defined in lines 4863 of Fig. 7.17) contains a series of member function calls that output a report summarizing the grades. Line 51 calls member function outputGrades to print the contents of the array grades. Lines 148150 in member function outputGrades use a for statement to output each student's grade. Although array indices start at 0, a professor would typically number students starting at 1. Thus, lines 149150 output student + 1 as the student number to produce grade labels "Student 1:", "Student 2:", and so on.
Member function processGrades next calls member function getAverage (lines 5455) to obtain the average of the grades in the array. Member function getAverage (declared in line 24 of Fig. 7.16 and defined in lines 98108) uses a for statement to total the values in array grades before calculating the average. Note that the averaging calculation in line 107 uses const static data member students to determine the number of grades being averaged.
Lines 5859 in member function processGrades call member functions getMinimum and getMaximum to determine the lowest and highest grades of any student on the exam, respectively. Let us examine how member function getMinimum finds the lowest grade. Because the highest grade allowed is 100, we begin by assuming that 100 is the lowest grade (line 68). Then, we compare each of the elements in the array to the lowest grade, looking for smaller values. Lines 7176 in member function getMinimum loop through the array, and lines 7475 compare each grade to lowGrade. If a grade is less than lowGrade, lowGrade is set to that grade. When line 78 executes, lowGrade contains the lowest grade in the array. Member function getMaximum (lines 8295) works similarly to member function getMinimum.
Finally, line 62 in member function processGrades calls member function outputBarChart to print a distribution chart of the grade data using a technique similar to that in Fig. 7.9. In that example, we manually calculated the number of grades in each category (i.e., 09, 1019, ..., 9099 and 100) by simply looking at a set of grades. In this example, lines 120121 use a technique similar to that in Fig. 7.10 and Fig. 7.11 to calculate the frequency of grades in each category. Line 117 declares and creates array frequency of 11 ints to store the frequency of grades in each grade category. For each grade in array grades, lines 120121 increment the appropriate element of the frequency array. To determine which element to increment, line 121 divides the current grade by 10 using integer division. For example, if grade is 85, line 121 increments frequency[ 8 ] to update the count of grades in the range 8089. Lines 124139 next print the bar chart (see Fig. 7.18) based on the values in array frequency. Like lines 2930 of Fig. 7.9, lines 135136 of Fig. 7.17 use a value in array frequency to determine the number of asterisks to display in each bar.
Testing Class GradeBook
The program of Fig. 7.18 creates an object of class GradeBook (Figs. 7.167.17) using the int array gradesArray (declared and initialized in lines 1011). Note that we use the binary scope resolution operator (::) in the expression "GradeBook::students" (line 10) to access class GradeBook's static constant students. We use this constant here to create an array that is the same size as array grades stored as a data member in class GradeBook. Lines 1314 pass a course name and gradesArray to the GradeBook constructor. Line 15 displays a welcome message, and line 16 invokes the GradeBook object's processGrades member function. The output reveals the summary of the 10 grades in myGradeBook.