7.6. 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 student grades. Previous versions of the class process grades entered by the user, but do not maintain the individual grade values in the class's data members. Thus, repeat calculations require the user to reenter the 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.16—therefore, each GradeBook object maintains its own set of grades.

Fig. 7.16. Definition of class GradeBook using an array to store test grades.

 

 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 <string> // 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'll soon see an example of a client program using this constant. Declaring students with the const qualifier indicates that this data member is constant—its 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 class—all 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 known as a data member—each 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 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 like any other data member. As you'll 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'll 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 17–24 of Fig. 7.17) has two parameters—the course name 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 array's values into the data member grades (lines 22–23 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 we discuss in Chapter 17, File Processing). In our test program, we simply initialize an array with a set of grade values (Fig. 7.18, lines 10–11). 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.

Fig. 7.17. GradeBook class member functions manipulating an array of grades.

 

 1   // Fig. 7.17: GradeBook.cpp
 2   // Member-function definitions for class GradeBook that
 3   // uses an array to store test grades.
 4   #include <iostream>
 5   using std::cout;
 6   using std::cin;
 7   using std::endl;
 8   using std::fixed;
 9
10   #include <iomanip>
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 gradesArray 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\n" << 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 << "\nClass average is " << setprecision( 2  ) << fixed <<
55         getAverage() << endl;
56
57      // call functions getMinimum and getMaximum
58      cout << "Lowest grade is " << getMinimum() << "\nHighest 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 << "\nGrade distribution:" << endl;
114
115     // stores frequency of grades in each range of 10 grades
116     const int frequencySize = 11;
117     int frequency[ frequencySize ] = {}; // initialize elements to 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 << "\nThe grades are:\n\n";
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

					  


Member function processGrades (declared in line 21 of Fig. 7.16 and defined in lines 48–63 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 148–150 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 149–150 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 54–55) 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 98–108 of Fig. 7.17) 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 58–59 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's 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 71–76 in member function getMinimum loop through the array, and lines 74–75 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 82–95) 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., 0–9, 10–19, . . ., 90–99 and 100) by simply looking at a set of grades. In this example, lines 120–121 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 120–121 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 80–89. Lines 124–139 next print the bar chart (see Fig. 7.18) based on the values in array frequency. Like lines 29–30 of Fig. 7.9, lines 135–136 of Fig. 7.17 use a value in array frequency to determine the number of asterisks to display in each bar.

Fig. 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

 

Welcome to the grade book for
CS101 Introduction to C++ Programming!

The grades are:

Student  1:  87
Student  2:  68
Student  3:  94
Student  4: 100
Student  5:  83
Student  6:  78
Student  7:  85
Student  8:  91
Student  9:  76
Student 10:  87

Class average is 84.90
Lowest grade is 68
Highest grade is 100

Grade distribution:
  0-9:
10-19:
20-29:
30-39:
40-49:
50-59:
60-69: *
70-79: **
80-89: ****
90-99: **
  100: *

					  


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 10–11). 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 13–14 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.