7.6 Case Study: Class GradeBook Using an array to Store Grades

We now present the first part of our case study on developing a GradeBook class that instructors can use to maintain students’ grades on an exam and display a grade report that includes the grades, class average, lowest grade, highest grade and a grade distribution bar chart. The version of class GradeBook presented in this section stores the grades for one exam in a one-dimensional array. In Section 7.9, we present a version of class GradeBook that uses a two-dimensional array to store students’ grades for several exams.

Storing Student Grades in an array in Class GradeBook

Figure 7.12 shows the output that summarizes the 10 grades we store in an object of class GradeBook (Fig. 7.13), which uses an array of integers to store the grades of 10 students for a single exam. The array grades is declared as a data member in line 142 of Fig. 7.13—therefore, each GradeBook object maintains its own set of grades.

Fig. 7.12 Output of the GradeBook example that stores grades in an array.

Alternate View

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: *

Fig. 7.13 Definition of class GradeBook that uses an array to store test grades.

Alternate View

 1   // Fig. 7.13: GradeBook.h
 2   // Definition of class GradeBook that uses an array to store test grades.
 3   #include <string>
 4   #include <array>
 5   #include <iostream>
 6   #include <iomanip> // parameterized stream manipulators
 7
 8   // GradeBook class definition
 9   class GradeBook {
10   public:
11      // constant number of students who took the test
12      static const size_t students{10}; // note public data
13
14      // constructor initializes courseName and grades array
15      GradeBook(const std::string& name,
16         const std::array<int, students>& gradesArray)
17         : courseName{name}, grades{gradesArray} {
18      }
19
20      // function to set the course name
21      void setCourseName(const std::string& name) {
22         courseName = name; // store the course name
23      }
24
25      // function to retrieve the course name
26      const std::string& getCourseName() const {
27         return courseName;
28      }
29
30      // display a welcome message to the GradeBook user
31      void displayMessage() const {
32         // call getCourseName to get the name of this GradeBook's course
33         std::cout << "Welcome to the grade book for
" << getCourseName()
34            << "!" << std::endl;
35      }
36
37      // perform various operations on the data (none modify the data)
38      void processGrades() const {
39         outputGrades(); // output grades array
40
41         // call function getAverage to calculate the average grade
42         std::cout << std::setprecision(2) << std::fixed;
43         std::cout << "
Class average is " << getAverage() << std::endl;
44
45         // call functions getMinimum and getMaximum
46         std::cout << "Lowest grade is " << getMinimum()
47            << "
Highest grade is " << getMaximum() << std::endl;
48
49         outputBarChart(); // display grade distribution chart
50      }
51
52      // find minimum grade
53      int getMinimum() const {
54         int lowGrade{100}; // assume lowest grade is 100
55
56         // loop through grades array
57         for (int grade : grades) {
58            // if current grade lower than lowGrade, assign it to lowGrade
59            if (grade < lowGrade) {
60               lowGrade = grade; // new lowest grade
61            }
62         }
63
64         return lowGrade; // return lowest grade
65      }
66
67      // find maximum grade
68      int getMaximum() const {
69         int highGrade{0}; // assume highest grade is 0
70
71         // loop through grades array
72         for (int grade : grades) {
73            // if current grade higher than highGrade, assign it to highGrade
74            if (grade > highGrade) {
75               highGrade = grade; // new highest grade
76            }
77          }
78
79          return highGrade; // return highest grade
80      }
81
82      // determine average grade for test
83      double getAverage() const {
84         int total{0}; // initialize total
85
86         // sum grades in array
87         for (int grade : grades) {
88            total += grade;
89         }
90
91         // return average of grades
92         return static_cast<double>(total) / grades.size();
93      }
94
95      // output bar chart displaying grade distribution
96      void outputBarChart() const {
97         std::cout << "
Grade distribution:" << std::endl;
98
99         // stores frequency of grades in each range of 10 grades
100        const size_t frequencySize{11};
101        std::array<unsigned int, frequencySize> frequency{}; // init to 0s
102
103        // for each grade, increment the appropriate frequency
104        for (int grade : grades) {
105           ++frequency[grade / 10];
106        }
107
108        // for each grade frequency, print bar in chart
109        for (size_t count{0}; count < frequencySize; ++count) {
110           // output bar labels ("0-9:", ..., "90-99:", "100:")
111           if (0 == count) {
112              std::cout << "  0-9: ";
113           }
114           else if (10 == count) {
115              std::cout << "  100: ";
116           }
117           else {
118              std::cout << count * 10 << "-" << (count * 10) + 9 << ": ";
119           }
120
121           // print bar of asterisks
122           for (unsigned int stars{0}; stars < frequency[count]; ++stars) {
123              std::cout << '*';
124           }
125
126           std::cout << std::endl; // start a new line of output
127       }
128     }
129
130     // output the contents of the grades array
131     void outputGrades() const {
132        std::cout << "
The grades are:

";
133
134        // output each student's grade
135        for (size_t student{0}; student < grades.size(); ++student) {
136           std::cout << "Student " << std::setw(2) << student + 1 << ": "
137              << std::setw(3) << grades[student] << std::endl;
138        }
139     }
140  private:
141     std::string courseName; // course name for this grade book
142     std::array<int, students> grades; // array of student grades
143  };

The size of the array in line 142 of Fig. 7.13 is specified by public static const data member students (declared in line 12), which is public so that it’s accessible to the class’s clients. 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—so in this particular implementation of class GradeBook, all GradeBook objects store grades for the same number of students. Recall from Section 3.3 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 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’s 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 scope resolution operator (::) and the name of the data member. You’ll learn more about static data members in Chapter 9.

Constructor

The class’s constructor (lines 15–18) has two parameters—the course name and a reference to an array of grades. When a program creates a GradeBook object (e.g., line 13 of Fig. 7.14), the program passes an existing int array to the constructor, which copies the array’s values into the data member grades (line 17 of Fig. 7.13). 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 14, File Processing). In our test program, we simply initialize an array with a set of grade values (Fig. 7.14, lines 9–10). 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. Note that the constructor receives both the string and the array by reference—this is more efficient than receiving copies of the original string and array. The constructor does not need to modify either the original string or array, so we also declared each parameter as const to ensure that the constructor does not accidentally modify the original data in the caller. We also declared function setCourseName to receives its string argument by reference.

Member Function processGrades

Member function processGrades (lines 38–50 of Fig. 7.13) contains a series of member function calls that output a report summarizing the grades. Line 39 calls member function outputGrades to print the contents of the array grades. Lines 135–138 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 136–137 output student + 1 as the student number to produce grade labels "Student 1: ", "Student 2: ", and so on.

Member Function getAverage

Member function processGrades next calls member function getAverage (line 43) to obtain the average of the grades. Member function getAverage (lines 83–93) totals the values in array grades before calculating the average. The averaging calculation in line 92 uses grades.size() to determine the number of grades being averaged.

Member Functions getMinimum and getMaximum

Lines 46–47 in 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 54). Then, we compare each of the elements in the array to the lowest grade, looking for smaller values. Lines 57–62 in member function getMinimum loop through the array, and line 59 compares each grade to lowGrade. If a grade is less than lowGrade, lowGrade is set to that grade. When line 64 executes, lowGrade contains the lowest grade in the array. Member function getMaximum (lines 68–80) works similarly to member function getMinimum.

Member Function outputBarChart

Finally, line 49 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.7. 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 Fig. 7.13, lines 104–106 use a technique similar to that in Fig. 7.8 and Fig. 7.9 to calculate the frequency of grades in each category. Line 100 declares and creates array frequency of 11 unsigned ints to store the frequency of grades in each grade category. For each grade in array grades, lines 104–106 increment the appropriate element of the frequency array. To determine which element to increment, line 105 divides the current grade by 10 using integer division. For example, if grade is 85, line 105 increments frequency[8] to update the count of grades in the range 80–89. Lines 109–127 next print the bar chart (see Fig. 7.12) based on the values in array frequency. Like lines 28–30 of Fig. 7.7, lines 122–124 of Fig. 7.13 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.14 creates an object of class GradeBook using the int array grades (declared and initialized in lines 9–10). The scope resolution operator (::) is used in the expression GradeBook::students (line 9) to access class GradeBook’s static constant students. We use this constant here to create an array that’s the same size as the array stored as a data member in class GradeBook. Line 11 declares a string representing the course name. Line 13 passes the course name and the array of grades to the GradeBook constructor. Line 14 displays a welcome message, and line 15 invokes the GradeBook object’s processGrades member function.

Fig. 7.14 Creates a GradeBook object’ using an array of grades, then invokes member function processGrades to analyze them.

Alternate View

 1   // Fig. 7.14: fig07_14.cpp
 2   // Creates GradeBook object using an array of grades.
 3   #include <array>
 4   #include "GradeBook.h" // GradeBook class  definition
 5   using namespace std;
 6
 7   int main() {
 8      // array of student grades
 9      const array<int, GradeBook::students> grades{
10         87, 68, 94, 100, 83, 78, 85, 91, 76, 87};
11      string courseName{"CS101 Introduction to C++ Programming"};
12
13      GradeBook myGradeBook(courseName, grades);
14      myGradeBook.displayMessage();
15      myGradeBook.processGrades();
16   }
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset