Polymorphism and Virtual Functions Andy Wang Object Oriented
Polymorphism and Virtual Functions Andy Wang Object Oriented Programming in C++ COP 3330
A Motivational Example In the previous example, we may have many subcategories of Students With many different types of grade reports It would be great to store one list of students, and print out ALL grade reports with a loop Student list[30000]; for (int j = 0; j < size; j++) list[i]. Grade. Report();
Problems… The items in the array are base-class Student objects Base class objects do not know about subtypes (Grads and Undergrads) No such information is stored in the array Everything in an array needs to be the same type and size The Grade. Report function being called is the Student class version Want to call the Grad version for graduate students and Undergrad version for undergraduate students Create separate arrays? Not realistic for many subtypes
A Base Class Pointer Property Normally, a pointer can only point to one type However, there is a special rule for inheritance A pointer to a base class type can be pointed at an object derived from the base class Similarly, a base class reference variable can refer to an object derived from that base class Examples Student s; Grad g; Undergrad u; Student *sp 1, *sp 2, *sp 3; sp 1 = &s; // pointing at Student object sp 2 = &g; // pointing at Grad object sp 3 = &u; // pointing at Undergrad object
Heterogeneous List A single array of pointers to a base class These pointers can point to any objects derived from that base Thus, we have created a list of various types of objects Without breaking the array rules All pointers have the same type—a pointer to the base class All pointers have the same size Student *list[30000]; list[0] = &g; list[1] = &u; list[2] = new Grad; …
Virtual Functions Which version of Grade. Report will be run? If we have the following: for (j = 0; j < size; j++) list[j]->Grade. Report(); This still calls the Student version of Grade. Report list[j]. Grade. Report is bound to the Student version at compile time Compiler cannot guess the version of object at run time If we want to run the correct version at runtime, we need to achieve late (dynamic) binding
Virtual Functions The keyword virtual will do the trick To override a base class function when it is called through a pointer, declare the function to be virtual class Student { public: virtual void Grade. Report(); }; Now when Grade. Report is called through a base-class pointer, the program will run the appropriate version
Example Student s; Grad g; Undergrad u; Student *sp 1, *sp 2, *sp 3; sp 1 = &s; // pointing at Student object sp 2 = &g; // pointing at Grad object sp 3 = &u; // pointing at Undergrad object sp 1 ->Grade. Report(); // runs Student’s version sp 2 ->Grade. Report(); // runs Grad’s version sp 3 ->Grade. Report(); // runs Undergrad’s version
Pure Virtual Function Suppose you do not want to do anything for the Student’s version of Grade. Report You can omit the definition virtual void Grade. Report()=0; A virtual function without a definition is a pure virtual function
Abstract Class Any class that has at least one pure virtual function is an abstract class An abstract class cannot be instantiated Abstract classes are generally used as base classes They are intended to be a place to declare data and functions common to classes derived from them Abstract class can still be used to build pointers, to take advantage of virtual functions Example abstract class Shape s; // illegal Shape *sptr; // legel
Employee Example http: //www. cs. fsu. edu/~myers/cop 3330/examples/inh er/employee/ Hierarchy of derived classes based on a class called Employee Goal is to store the employee information and handle paycheck printing Notice the virtual Print. Check function and the building of heterogeneous list
employee. h #ifndef _EMPLOYEE_H #define _EMPLOYEE_H class Employee { public: virtual void Print. Check()=0; protected: float net. Pay; Employee(); Employee(char *n, char *a, char *ssn); char name[30]; char address[90]; char soc. Sec. Number[12]; };
employee. h class Temporary : public Employee { public: Temporary(char *n, char *a, char *ssn, float hw, float hr); Temporary(); void Print. Check(); private: float hours. Worked; float hourly. Rate; };
employee. h class Permanent: public Employee { public: static float benefit. Deduction; protected: Permanent(char *n, char *a, char *ssn); Permanent(); void Print. Check()=0; };
employee. h class Hourly : public Permanent { public: Hourly(char *n, char *a, char *ssn, float hw, float hr); Hourly(); void Print. Check(); private: float hours. Worked; float hourly. Rate; };
employee. h class Salaried: public Permananet { public: Salaried(char *n, char *a, char *ssn, float wp); Salaried(); void Print. Check(); private: float weekly. Pay; }; #endif
What’s going on?
employee. cpp #include <iostream> #include <cstring> #include “employee. h” using namespace std; float Permanent: : benefit. Deduction=100. 00; Employee: : Employee() { cout << “n. Type employee name, followed by <Enter>: “; cin. getline(name, 30); cout << “n. Type employee address, followed by <Enter>: “; cin. getline(address, 80); cout << “n”type employee social security number, followed by <Enter>: “; cin. getline(soc. Sec. Number, 12); }
employee. cpp Employee: : Employee(char *n, char *a, char *ssn) { strcpy(name, n); strcpy(address, a); strcpy(soc. Sec. Number, ssn); } Temporary: : Temporary() { cout << “n. Type number of hours worked, followed by <Enter>: “; cin >> hours. Worked; cout << “n. Type hourly rate, followed by <Enter>: “; cin >> hourly. Rate; }
employee. cpp Temporary: : Temporary(char *n, char *a, char *ssn, float hw, float hr): Employee(n, a, ssn) { hours. Worked = hw; hourly. Rate = hr; }
employee. cpp void Temporary: : Print. Check() { net. Pay = hours. Worked*hourly. Rate; cout << "nn__________________"; cout << "nn. PAY TO THE ORDER OF: " << 't' << name; cout << "nttt" << address; cout << "nttt" << soc. Sec. Number << 'n'; cout << "n. EMPLOYEE CLASS: Temporary"; cout << "nn. HOURS: " << hours. Worked; cout << "n. RATE: " << hourly. Rate; cout << "nn. THE AMOUNT OF *****$" << net. Pay << 'n'; cout << "nn________________nn"; };
employee. cpp Permanent: : Permanent() { } Permanent: : Permanent(char* n, char* a, char* ssn): Employee(n, a, ssn) { } Hourly: : Hourly() { cout << "n. Type number of hours worked, followed by <Enter>: "; cin >> hours. Worked; cout << "n. Type hourly rate, followed by <Enter>: "; cin >> hourly. Rate; };
employee. cpp Hourly: : Hourly(char* n, char* a, char* ssn, float hw, float hr): Permanent(n, a, ssn) { hours. Worked=hw; hourly. Rate=hr; };
employee. cpp void Hourly: : Print. Check() { net. Pay = (hours. Worked*hourly. Rate) - benefit. Deduction; cout << "nn__________________"; cout << "nn. PAY TO THE ORDER OF: " << 't' << name; cout << "nttt" << address; cout << "nttt" << soc. Sec. Number << 'n'; cout << "n. EMPLOYEE CLASS: Hourly"; cout << "nn. BENEFITS DEDUCTION: " << benefit. Deduction; cout << "n. HOURS: " << hours. Worked; cout << "n. RATE: " << hourly. Rate; cout << "nn. THE AMOUNT OF *****$" << net. Pay << 'n'; cout << "nn_________________nn"; };
employee. cpp // Other member data is solicited by the constructors for // Permanent and Employee classes Salaried: : Salaried() { cout << "n. Type weekly salary, followed by <Enter>: "; cin >> weekly. Pay; }; Salaried: : Salaried(char* n, char* a, char* ssn, float wp): Permanent(n, a, ssn) { weekly. Pay=wp; };
employee. cpp void Salaried: : Print. Check() { net. Pay = weekly. Pay - benefit. Deduction; cout << "nn___________________"; cout << "nn. PAY TO THE ORDER OF: " << 't' << name; cout << "nttt" << address; cout << "nttt" << soc. Sec. Number << 'n'; cout << "n. EMPLOYEE CLASS: Salaried"; cout << "nn. BENEFITS DEDUCTION: " << benefit. Deduction; cout << "n. SALARY: " << weekly. Pay; cout << "nn. THE AMOUNT OF *****$" << net. Pay << 'n'; cout << "nn_________________nn"; };
utility. h #ifndef _UTILITY_H #define _UTILITY_H void Terminate(); // Ask if the user is ready to quit; return 1 if ‘y’ or ‘Y’ int Ready. To. Quit(); // Freeze the screen until the user types a character void Wait. For. User(); // Set C++ format flags for numeric output void Set. Numeric(void); #endif
utility. cpp #include <iosteram> #include <cctype> #include <iomanip> #include “utility. h” using namespace std; void Terminate() { char any; cout << “n. Press ‘x’ followed by ENTER to exit the program…”; cin >> any; cout << “n. PROCESSING COMPLETED … GOOD EYE”; }
utility. cpp int Ready. To. Quit() { char ans; cout << "n. Do you wish to run the program again (Y for yes, N for no)? "; cin >> ans; ans = toupper(ans); while ((ans !='Y') && (ans!='N')) { cout << "n. Please answer again with Y or N"; cout << "nt. Run the program again? "; cin >> ans; ans = toupper(ans); } return(ans =='N'); // returns 1 when ready to quit }
utility. cpp void Wait. For. User(void) { cout << "n. Press 'c' followed by Enter to continue. . . "; char any; cin >> any; cin. get(); // pick up newline cout << 'n'; } void Set. Numeric(void) { cout << setw(8) << setprecision(2); cout. setf(ios: : fixed, ios: : floatfield); cout. setf(ios: : showpoint); }
main. cpp #include <iostream> #include “employee. h” #include “utility. h” using namespace std; int main() { Employee *emps[6]; // array of Employee pointers cout << “nn. Creating a temporary employee pay record…”; Temporary t(“Clipper Decker”, “Clinton, NY”, “ 123456789”, 40. 0, 5. 25); emps[0] = &t;
main. cpp cout << “nn. Creating an hourly employee pay record…”; Hourly h(“”Sparky Hirshfield”, “Deansboro, NY”, “ 234567890”, 30. 5, 8. 5); emps[1] = &j; cout << “nn. Creating a salaried employee pay record…”; Salaried s(“Fenton Sugarman”, “Boston, MA”, “ 345678901”, 500. 00); emps[2] = &s; Wait. For. User();
main. cpp cout << “nn. Enter data for a temporary employee pay record…”; Temporary *t. Emp = new Temporary(“Bob”, “Here”, “ 111 -11 -1111”, 45. 0, 15. 45); emps[3] = t. Emp; cout << “nn”Enter data for an hourly employee pay record…”; Hourly *h. Emp = new Hourly; emps[4] = h. Emp; cout << “nn. Enter data for a salaried employee pay record…”; if (cin. peek() == ‘n’) cin. get(); emps[5] = new Salaried;
main. cpp Set. Numeric(); for (int j = 0; j < 6; j++) { cout << “nn”; emps[i]->Printcheck(); cout << “nn”; Wait. For. User(); } Terminate(); cout << ‘n’; return 0; }
- Slides: 34