Lab #29


Objective: To practice overloading operators, using friend functions, and using a class within a class.
  1. Overloading input and output operators

    1. Consider the following source code files:
      // Student.h
      // Student class header file
      
      #ifndef STUDENT_H
      #define STUDENT_H
      
      #include <string>
      using namespace std;
      
      class Student {
         public:
      
            // accessors:
      
            string getName();
            // pre:  none
            // post: returns theName
      
            double getAverage();
            // pre:  none
            // post: returns average
      
            char getGrade();
            // pre:  none
            // post: returns grade
      
            // mutators:
      
            void setName (string name);
            // pre:  none
            // post: sets theName equal to name
      
            void setAverage (double avg);
            // pre:  none
            // post: sets average equal to avg
      
            void setGrade (char letterGrade);
            // pre:  none
            // post: sets grade equal to letterGrade
      
            void readStudent (istream& fin);
            // pre:  fin is open for input, and contains the information in this form:
            //      firstName MI lastName
            //      average
            //      grade
            // post: information has been read into calling object
      
            void displayStudent (ostream& fout);
            // pre:  fout is open for output
            // post: information for calling object is displayed to fout, one item per line
      
         private:
      
            string theName;
            double average;
            char grade;
      };
      
      #endif
      
      // Student.cpp
      
      #include "Student.h"
      
      string Student::getName() {
         return theName;
      }
      
      double Student::getAverage() {
         return average;
      }
      
      char Student::getGrade() {
         return grade;
      }
      
      void Student::setName (string name) {
         theName = name;
      }
      
      void Student::setAverage (double avg) {
         average = avg;
      }
      
      void Student::setGrade (char letterGrade) {
         grade = letterGrade;
      }
      
      void Student::readStudent (istream& fin) {
      
         while (isspace (fin.peek()))
            fin.ignore();
      
         getline (fin, theName);
         fin >> average;
         fin >> grade;
      }
      
      void Student::displayStudent (ostream& fout) {
         fout << theName << endl;
         fout << average << endl;
         fout << grade << endl;
      }
      
      // driver.cpp
      // tests Student class
      
      #include <iostream>
      #include <fstream>
      using namespace std;
      #include "student.h"
      
      main() {
      
         Student student1, student2, student3, student4;
      
         ifstream fin;
      
         fin.open ("student.txt");
      
         if (fin.fail()) {
            cout << "Error opening input file.\n";
            exit (1);
         }
      
         // read information for each student from file
         student1.readStudent (fin);
         student2.readStudent (fin);
         student3.readStudent (fin);
         student4.readStudent (fin);
        
         // display each student here:
         student1.displayStudent (cout);
         student2.displayStudent (cout);
         student3.displayStudent (cout);
         student4.displayStudent (cout);
      
         // test less than operator
      
         /*
         if (student1 < student2)
           cout << student1.getName() << " is less than " << student2.getName() << endl;
         else
           cout << student1.getName() << " is not less than " << student2.getName() << endl;
         
         if (student3 < student4)
            cout << student3.getName() << " is less than " << student4.getName() << endl;
         else
            cout << student3.getName() << " is not less than " << student4.getName() << endl;
      
         cout << endl;
      
          */
      
         // test less than or equal to operator
        
         // test greater than operator
      
         // test greater than or equal to operator
      
         // test equal to operator
       
         // test not equal to operator
      }
      
      Sarah M Smith
      82
      B
      John A Smith
      84
      B
      Michael C Cook
      72
      C
      michael c cook
      92
      A
      
      Rather than using functions called readStudent and displayStudent, it would be convenient to use the input and output operators for a Student in the same way we use them for the predefined types, such as integers and strings.

    2. Remove the readStudent declaration from Student.h, and replace it with the following declaration:
      friend istream& operator >> (istream& fin, Student& s);
      // pre:  fin is open for input and contains information in this form:
      //       firstName  MI  lastName
      //       average
      /        grade
      // post: information from fin has been read into s
      
    3. Modify the function header for readStudent in Student.cpp file so that it corresponds to the declaration above. Do not use the word friend in the .cpp file, and do not use the class name and scope resolution operator.

    4. Since this is not a member function, the private variables need to be preceded with the member object s that is passed as a parameter. We also need to return fin. Modify the function accordingly.

    5. Change the driver program so that the input operator is used, rather than the readStudent function.

    6. Replace the displayStudent function with a function that overloads the >> operator (follow the steps above, but use ostream& rather than istream&).

    7. Change the driver so that the output operator is used, rather than the displayStudent function.

  2. Overloading comparison operators

    1. In Student.h, write a declaration for a less than (<) operator function. The postcondition should state that the function returns true if the calling object's student name is less than the student name of the parameter; else the function returns false.

    2. Implement the < operator function in the Student.cpp file.

    3. Uncomment out the code in the driver that tests this operator for student1 and student2, and also the test for student3 and student4.

    4. Modify the < operator function so that it is not case-sensitive. (Hint: write a function that takes the name as a parameter and returns the lower-case version. Call this function for both names at the beginning of the function, and use those converted names in your less-than test).

    5. Overload the other comparison operators (>, <=, >=, ==, !=).

    6. Add code to your driver to test each of the comparison operators, first for students 1 and 2, and again for students 3 and 4.

  3. Using a class within a class

    1. Consider the following source code files:
      // FullName.h
      #ifndef FULLNAME_H
      #define FULLNAME_H
      
      #include <string>
      using namespace std;
      
      struct FullName {
         string firstName;
         char MI;
         string lastName;
      };
      
      #endif
      
      // Student.h
      // Student class header file
      
      #ifndef STUDENT_H
      #define STUDENT_H
      
      #include <string>
      using namespace std;
      #include "Fullname.h"
      
      class Student {
         public:
      
            // accessors:
            FullName getName() const;
            // pre:  none
            // post: returns theName
      
            double getAverage() const;
            // pre:  none
            // post: returns average
      
            string getGrade() const;
            // pre:  none
            // post: returns grade
      
            // mutators:
            void setName (FullName name);
            // pre:  none
            // post: sets theName equal to name
      
            void setAverage (double avg);
            // pre:  none
            // post: sets average equal to avg
      
            void setGrade (string letterGrade);
            // pre:  none
            // post: sets grade equal to letterGrade
      
            friend istream& operator >> (istream& fin, Student& theStudent);
            // pre:  fin is open for input and contains information in the following format:
            //       firstName MI lastName
            //       average
            //       grade
            // post: information for theStudent has been read from fin
      
            friend ostream& operator << (ostream& fout, const Student& theStudent);
            // pre:  fout is open for output
            // post: theStudent is output to fout in the following format:
            //      name
            //      average
            //      grade
      
         private:
      
            FullName theName;
            double average;
            string grade;
      };
      
      #endif
      
      // student.cpp
      // revised to work with or without middle initial in input
      
      #include <iostream>
      using namespace std;
      #include "student.h"
      
      FullName Student::getName() const {
         return theName;
      }
      
      double Student::getAverage() const {
         return average;
      }
      
      string Student::getGrade() const {
         return grade;
      }
      
      void Student::setName (FullName name) {
         theName = name;
      }
      
      void Student::setAverage(double avg) {
         average = avg;
      }
      
      void Student::setGrade(string letterGrade) {
         grade = letterGrade;
      }
      
      istream& operator >> (istream& fin, Student& theStudent) {
         // read first name and middle initial
         fin >> theStudent.theName.firstName;
         fin >> theStudent.theName.MI;
      
        // if middle initial isn't followed by a space, put it back
         // and set middle initial to a space (no middle initial)
         if (fin.peek() != '.') {
            fin.putback (theStudent.theName.MI);
            theStudent.theName.MI = ' ';
      
         } else  // throw away the period
              fin.ignore();
      
         // read lastname
         fin >> theStudent.theName.lastName;
      
         // read average and grade
         fin >> theStudent.average;
         fin >> theStudent.grade;
      
         return fin;
      }
      
      ostream& operator << (ostream& fout, const Student& theStudent) {
      
         fout << theStudent.theName.firstName << " ";
      
         if (theStudent.theName.MI != ' ')
            fout << theStudent.theName.MI << " ";
      
         fout << theStudent.theName.lastName << endl;
         fout << theStudent.average << endl;
         fout << theStudent.grade << endl;
      
         return fout;
      }
      
      // driver.cpp
      // tests Student class
      
      #include <iostream>
      #include <fstream>
      using namespace std;
      #include "student.h"
      
      main() {
         Student UD_Student;
      
         ifstream fin; 
         fin.open ("input.txt");
      
         if (fin.fail()) {
            cout << "Error opening input file.\n";
            exit (1); 
         }    
      
         ofstream fout;
         fout.open ("lab23.out");
      
         if (fout.fail()) {
            cout << "Error opening output file.\n";
            exit (1); 
         }    
        
         fin >> UD_Student;
         while (!fin.fail()) {
            fout << UD_Student << endl;
            fin >> UD_Student;
         }    
      
         fin.close();
      }
      
      Kelly Carter
      95
      A
      Maria C. Pons
      82
      B
      Zachary Taylor
      78
      C
      Manfred I. Werner
      65
      D
      Simon B. Schuster
      88
      B
      Betsy M. Ross
      75
      C
      David O. Copperfield
      56
      F
      Bruce Lee
      90
      A
      
    2. Modify FullName.h
      1. Change FullName so that it is a class rather than a structure. The variables firstName, MI, and lastName should be private variables; write declarations for an accessor and a mutator function for each variable.
      2. Create a new source file, FullName.cpp, and implement the accessor and mutator functions for FullName in this file.

    3. Test your class
      1. Compile your program. Explain why you are getting error messages regarding the student input and output functions that you did not get when FullName was a structure.

    4. Modify Student.cpp
      1. Change the implementation of the input function so that it works even though FullName is now a class.
      2. Change the implementation of the output function so that it works correctly.

      Notice that the driver program did not need to be changed even though the implementation of the Student class changed.

    5. Modify the FullName class and Student.cpp
      1. Overload the >> (input) operator for the FullName class. It should read firstName, MI, lastName, or just firstName and lastName if there is no middle initial.
      2. Overload the << (output) operator for the FullName class. It should output firstName, MI, lastname, or just firstName and lastName if there is no middle initial.
      3. Change the input function in the Student class so that, rather than reading each field for theName, it simply uses the >> operator for theName.
      4. Change the output function in the Student class so that, rather than outputting each field for theName, it simply uses the << operator for theName.

Return Home