1 / 73

Understanding Polymorphism in Programming: Part I Summary

Learn about polymorphism, including the 'is-a' relationship in derived classes, public inheritance, constructors/destructors, and method redefinition. Explore static and dynamic binding, 'over-loading,' and 'over-riding' concepts in class hierarchies.

Download Presentation

Understanding Polymorphism in Programming: Part I Summary

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Polymorphism: Part I

  2. Summary: Derived Class ‘is-a’ relationship • Different from ‘has-a’ or ‘uses-a’, or … by class-in-class Public inheritance • Public • Private ‘protected’ Constructors/destructors Redefinition (over-riding)  … COMP152 2

  3. What is Polymorphism? Polymorphism: poly + morph (biology): the existence of two or more forms of individuals within the same animal species In programming languages Objects belonging to different types (classes) call methods of the same name, but of different behavior. ‘over-loading’ is one type of polymorphism: static binding • Same names, but different arguments ‘over-riding’ by ‘virtual function’ is another type: dynamic binding • Same names, but different ‘classes’ with the same hierarchy 3 COMP152

  4. Polymorphism with inheritance hierarchies “Program in the general” vs. “program in the specific” Process objects of classes that are part of the same hierarchy as if they are objects of a single class • E.g., vehicles  4-wheel vehicle  passenger car  sport car • Objects can be created in any part of the chain of hierarchy Each object performs the correct tasks for that object’s type • Different actions occur depending on the type of object New classes can be added with little or no modification to existing code COMP152 4

  5. class Employee { double pay(); … } class Manager : public Employee { double pay(); } double Employee::pay() { … } double Manager::pay() { return 10*Employee::pay(); } int main () { Employee e; Manager m; cout << e.pay(); cout << m.pay(); }

  6. Motivation • the pointers/references are logically ‘compatible’ for the base and derived classes • with ‘static typing or binding’, at compilation, it can only be fixed based on the ‘type/class’ of the pointers/references, but not the actual objects (base or derived class) • the ‘resolution’ should be delayed to the ‘run-time’, so to introduce ‘dynamic binding’ • ‘virtual function’ explicitly enforces ‘dynamic binding’

  7. Pointers and Classes • A ‘manager’ is an ‘emloyee’, so a base class pointer (employee) can point to a ‘manager’ object • But, a derived class pointer (Manager) CANNOT point to a base class object (employee) as an ‘employee’ is not necessarily a ‘manager’! int main() { Employee* ep; ep = new Employee; // OK … ep = new Manager; // OK … Manager* mp; mp = new Manager; // OK … mp = new Employee; // error! } class Employee { double pay(); … } class Manager : public Employee { double pay(); }

  8. Good Pointers, but Wrong Functions! class Employee { double pay(); … } class Manager : public Employee { double pay(); } void printPay(const Employee& e) { cout << e.pay() << endl; } int main() { Employee e; Manager m; printPay(e); // ok for ‘employee’ printPay(m); // ??? for ‘manager’ ??? } int main() { Employee* ep; ep = new Employee; … ep = new Manager; … ep->pay()? // always Employee::pay(), // never Manager::pay()!!!

  9. ‘Virtual’ is polymorphic class Employee { virtual double pay(); … } class Manager : public Employee { double pay(); } Employee* ep; ep = new Employee; … ep = new Manager; … ep->pay(); // always the right one, // either Employee::pay(), or Manager::pay()!!!

  10. Static and Dynamic Binding The compiler determines which version of the function or class to use during the compilation time for • Function overloading • Function and class template substantiations Which version is called must be deferred to run time • This is dynamic binding COMP152 10

  11. (non-virtual) Static and (virtual) Dynamic Binding Non-virtual member functions are resolved statically at compilation time • i.e. the member function is selected based on the type of the pointer (or reference) to the object Virtual member functions are resolved dynamically at run time • i.e. the member function is selected based on the type of the object! COMP152 11

  12. Example with more details

  13. Invoking functions with pointers/references • Cannot aim derived-class pointer to a base-class object • Aim base-class pointer at base-class object • Invoke base-class functionality • Aim derived-class pointer at derived-class object • Invoke derived-class functionality • Aim base-class pointer at derived-class object • Because derived-class object is an (inherited) object of base class • Can only invoke base-class functionalities • Invoked functionality depends on the pointer/reference type used to invoke the function (which is base or derived object). • Therefore, if it is base pointer, even if it points to a derived-class object, it invokes the functionality of base class CommissionEmployee1.h, CommissionEmployee1.cpp, BasePlusCommissionEmployee1.h, BasePlusCommissionEmployee1.cpp, test1a.cpp COMP152 13

  14. CommissionEmployee1.h class CommissionEmployee { public: CommissionEmployee( const string &, const string &, const string &, double = 0.0, double = 0.0 ); void setFirstName( const string & ); // set first name string getFirstName() const; // return first name ... double earnings() const; // calculate earnings void print() const; // print CommissionEmployee object Function earnings and print will be redefined in derived classes to calculate the employee’s earnings Function print will be redefined in derived class to print the employee’s information COMP152 14

  15. BasePlusCommissionEmployee1.h class BasePlusCommissionEmployee : public CommissionEmployee { public: BasePlusCommissionEmployee( const string &, const string &, const string &, double = 0.0, double = 0.0, double = 0.0 ); void setBaseSalary( double ); // set base salary double getBaseSalary() const; // return base salary double earnings() const; // calculate earnings void print() const; // print BasePlusCommissionEmployee object private: double baseSalary; // base salary }; // end class BasePlusCommissionEmployee Redefine functions earnings and print COMP152 15

  16. Test1a.cpp sample output (1/2) Print base-class and derived-class objects: commission employee: Sue Jones social security number: 222-22-2222 gross sales: 10000.00 commission rate: 0.06 base-salaried commission employee: Bob Lewis social security number: 333-33-3333 gross sales: 5000.00 commission rate: 0.04 base salary: 300.00 Calling print with base-class pointer to base-class object invokes base-class print function: commission employee: Sue Jones social security number: 222-22-2222 gross sales: 10000.00 commission rate: 0.06 COMP152 16

  17. Test1a.cpp sample output (2/2) Calling print with derived-class pointer to derived-class object invokes derived-class print function: base-salaried commission employee: Bob Lewis social security number: 333-33-3333 gross sales: 5000.00 commission rate: 0.04 base salary: 300.00 Calling print with base-class pointer to derived-class object invokes base-class print function on that derived-class object: commission employee: Bob Lewis social security number: 333-33-3333 gross sales: 5000.00 commission rate: 0.04 COMP152 17

  18. The pointer must be a base-class pointer, pointing to a derived-class object • All the base class functions of the derived object can be called. This is not a problem because derived class inherits all the functions from the base class. • Because it is a base class pointer, cannot access the members of derived-class even if the base-class pointer is pointing to the derived-class object Aim a derived-class pointer at a base-class object is an error • C++ compiler generates error • CommissionEmployee (base-class object) is not a BasePlusCommissionEmployee (derived-class object) • This is because • A derived-class pointer is supposed to be able to access all the derived-class member functions that it points to • If the pointer is pointing to a base class, some of these derived-class functions may not even be available at the base class COMP152 18

  19. Test1b.cpp CommissionEmployee commissionEmployee( "Sue", "Jones", "222-22-2222", 10000, .06 ); BasePlusCommissionEmployee *basePlusCommissionEmployeePtr = 0; // aim derived-class pointer at base-class object // Error: a CommissionEmployee is not a BasePlusCommissionEmployee basePlusCommissionEmployeePtr = &commissionEmployee; Cannot assign base-class object to derived-class pointer COMP152 19

  20. // aim base-class pointer at derived-class object commissionEmployeePtr = &basePlusCommissionEmployee; // invoke base-class member functions on derived-class // object through base-class pointer (allowed) string firstName = commissionEmployeePtr->getFirstName(); tester1c: Aiming base-class pointer at derived-class object • Calling functions that exist in base class causes base-class functionality to be invoked • Calling functions that do not exist in base class (may exist in derived class) will result in error • Derived-class members cannot be accessed from base-class pointers COMP152 20

  21. CommissionEmployee2.h class CommissionEmployee { public: CommissionEmployee( const string &, const string &, const string &, double = 0.0, double = 0.0 ); void setFirstName( const string & ); // set first name string getFirstName() const; // return first name ... virtual double earnings() const; // calculate earnings virtual void print() const; // print CommissionEmployee object Declaring earnings and print as virtual allows them to be overridden • Overridden means superceding the base class codes • Not redefined, meaning that the original function of the base class still exists COMP152 21

  22. BasePlusCommissionEmployee2.h class BasePlusCommissionEmployee : public CommissionEmployee { public: BasePlusCommissionEmployee( const string &, const string &, const string &, double = 0.0, double = 0.0, double = 0.0 ); void setBaseSalary( double ); // set base salary double getBaseSalary() const; // return base salary virtual double earnings() const; // calculate earnings virtual void print() const; // print private: double baseSalary; // base salary }; // end class BasePlusCommissionEmployee Functions earnings and print are already virtual – good practice to declare virtual even with overriding function (though optional) COMP152 22

  23. tester2.cpp (1/3) // output objects using static binding cout << "Invoking print function on base-class and derived-class " << "\nobjects with static binding\n\n"; commissionEmployee.print(); // static binding basePlusCommissionEmployee.print(); // static binding // output objects using dynamic binding cout << "\n\n\nInvoking print function on base-class and “ << "derived-class \nobjects with dynamic binding"; // aim base-class pointer at base-class object and print commissionEmployeePtr = &commissionEmployee; cout << "\n\nCalling virtual function print with base-class pointer" << "\nto base-class object invokes base-class " << "print function:\n\n"; commissionEmployeePtr->print(); // invokes base-class print Aiming base-class pointer at base-class object and invoking base-class functionality COMP152 23

  24. tester2.cpp (2/3) // aim derived-class pointer at derived-class object and print basePlusCommissionEmployeePtr = &basePlusCommissionEmployee; cout << "\n\nCalling virtual function print with derived-class “ << "pointer\nto derived-class object invokes derived-class “ << "print function:\n\n"; basePlusCommissionEmployeePtr->print(); Aiming derived-class pointer at derived-class object and invoking derived-class functionality COMP152 24

  25. tester2.cpp (3/3) // aim base-class pointer at derived-class object and print commissionEmployeePtr = &basePlusCommissionEmployee; cout << "\n\nCalling virtual function print with base-class pointer" << "\nto derived-class object invokes derived- class " << "print function:\n\n"; // polymorphism; invokes BasePlusCommissionEmployee's print; // base-class pointer to derived-class object commissionEmployeePtr->print(); Aiming base-class pointer at derived-class object and invoking derived-class functionality via polymorphism and virtual functions COMP152 25

  26. tester2.cpp Sample Output (1/3) Invoking print function on base-class and derived-class objects with static binding commission employee: Sue Jones social security number: 222-22-2222 gross sales: 10000.00 commission rate: 0.06 base-salaried commission employee: Bob Lewis social security number: 333-33-3333 gross sales: 5000.00 commission rate: 0.04 base salary: 300.00 Invoking print function on base-class and derived-class objects with dynamic binding COMP152 26

  27. tester2.cpp Sample Output (2/3) Calling virtual function print with base-class pointer to base-class object invokes base-class print function: commission employee: Sue Jones social security number: 222-22-2222 gross sales: 10000.00 commission rate: 0.06 Calling virtual function print with derived-class pointer to derived-class object invokes derived-class print function: base-salaried commission employee: Bob Lewis social security number: 333-33-3333 gross sales: 5000.00 commission rate: 0.04 base salary: 300.00 COMP152 27

  28. tester2.cpp Sample Output (3/3) Calling virtual function print with base-class pointer to derived-class object invokes derived-class print function: base-salaried commission employee: Bob Lewis social security number: 333-33-3333 gross sales: 5000.00 commission rate: 0.04 base salary: 300.00 COMP152 28

  29. Pure Virtual Functions and Abstract Classes

  30. Abstract Classes ‘Employee’: both base class and derived class are useful objects ‘Shape’ represents an abstract concept for which objects cannot exist. ‘shape’ makes sense only as the base of some class derived from it. class Shape { void rotate(int); void draw(); … } class Circle : public Shape { … } Shape s; ??? class Employee { string firstName; string familyName; … } class Manager : public Employee { list<Employee*> group; … }

  31. Virtual Functions A ‘virtual’ function in a base class will be redefined in each derived class class Shape { virtual void rotate(int); virtual void draw(); … } class Circle : public Shape { public: void rotate(int); void draw(); … private: int radius; }

  32. ‘Pure’ Virtual Functions A ‘virtual’ function is ‘made pure’ by the initializer = 0. class Shape { virtual void rotate(int) = 0; virtual void draw() = 0; … } class Circle : public Shape { public: void rotate(int); void draw(); … private: int radius; }

  33. Abstract Class A class with one or more pure virtual functions is an ‘abstract’ class, And no objects of that abstract class can be created! An abstract class is only used as an interface and as a base for other classes. class Shape { virtual void rotate(int) = 0; virtual void draw() = 0; … } class Circle : public Shape { public: void rotate(int); void draw(); … private: int radius; } Shape s; // error!!! Circle c;

  34. A pure virtual function that is not defined in a derived class remains a pure virtual function, so the derived class is still an abstract class. class Shape { virtual void rotate(int) = 0; virtual void draw() = 0; … } class Polygon : public Shape { public: bool is_closed() { return true; } } Polygon p; // error!!!

  35. Polymorphism: Part II

  36. A Test: What is the Output? int main(){ A* z = new A; z->f(); delete z; A* x = new B; x->f(); delete x; A* y = new C; y->f(); delete y; return 0; } #include <iostream> using namespace std; class A { public: A() {} void f() {cout << "A::f()" << endl;} }; class B: public A { public: B() {} void f() {cout << "B::f()" << endl;} }; class C: public B { public: C() {} void f() {cout << "C::f()" << endl;} }; COMP152 36

  37. Output: A::f() A::f() A::f() COMP152 37

  38. Change A* to B* or C*? COMP152 38

  39. A Test:if we add virtual to class A? class A { public: A() {} virtual void f() {cout << "A::f()" << endl;} }; COMP152 39

  40. Output: A::f() B::f() C::f() COMP152 40

  41. The three corner-stones of OOP • Encapsulation • Inheritance • Polymorphism

  42. Encapsulation Languages such as Pascal and C facilitated development of structured programs Need for ability to extend and reuse software became evident • This leads to object-oriented programming where objects are built on top of other objects Data and basic operations for processing the data are encapsulated into a single “entity”. This is made possible with introduction of • Modules • Libraries • Packages Implementation details are separated from class definition • Client code must use only public operations • Implementation may be changed without affecting client code COMP152 42

  43. Encapsulation with Inheritance Some basic class features may be re-used in other classes A class can be derived from another class • New class inherits data and function members from the original class • Reusable for the new class Example: • Consider the need to add, for example max() and min(), functions to a stack • Could simply add these functions to the class • But … alters already proven code It is better to build “on top” of the proven stack by adding the functions • The new class is inherited or derived from the stack class • Obviously, this concept is different from creating a new class with a stack as its member object COMP152 43

  44. Inheritance Features and Advantages Software reusability • Often used in computer game design Create new class from existing class • Seamlessly absorb existing class’s data and behaviors • Enhance with new capabilities Derived class inherits from base class • More specialized group of objects (e.g., a character moves, but a soldier and a giant move differently) • Behaviors inherited from base class • Can customize • Additional behaviors COMP152 44

  45. Pure Virtual Functions and Abstract Classes We can use the abstract base class to declare pointers and references • Can point to objects of any concrete class derived from the abstract class • Programs typically use such pointers and references to manipulate derived-class objects polymorphically Polymorphism is particularly effective for implementing software systems • Reading or writing data from and to different devices of the same base class Iterator class • Can traverse all the objects in a container COMP152 45

  46. #include <iostream> using namespace std; class base{ public: virtual void print() = 0; virtual void print2() = 0; }; class derived1: public base{ public: virtual void print(){ cout << "derived1\n"; } virtual void print2(){} // must have this line, // otherwise compiler complains in main() }; class derived2: public base{ public: virtual void print(){ cout << "in derived2\n"; } // do not need to define print2() here as // derived2 is not a concrete class }; class derived3: public derived2{ public: virtual void print2(){ cout << "In derived3\n"; } }; int main(){ derived1 d1; // derived2 d2; compiler complains: // the following virtual functions are abstract: // void base::print2() derived3 d3; d1.print(); d3.print(); // can do that! d3.print2(); return 1; } derived1 in derived2 In derived3 COMP152 46

  47. dyanmic_cast dynamic_cast<T*>(p); p is a pointer or reference We can always cast a (derived) class to one of its base classes If p is a base class pointer, T* is a derived class pointer • Statically, it is impossible (static_cast fails!) • But dynamically at run-time, we look at the object pointed to by p (if any). • If the object is of class T, then returns a pointer type T* to that object • Otherwise, 0 is returned. COMP152 47

  48. #include <iostream> #include <typeinfo> #include <string> using namespace std; class base{ public: virtual void print(){ cout << "Base object\n";} }; class derived: public base{ public: virtual void print(){ cout << "Derived object\n"; } }; int main(){ base* bptr[2]; // check whether it points to a derived obj derived* is_derived; bptr[0] = new base(); bptr[1] = new derived(); // check whether the pointer can be successfully cast is_derived = dynamic_cast<derived*> (bptr[0]); if(is_derived) cout << "bptr[0] is a derived object.\n"; else cout << "bptr[0] is a base object.\n"; is_derived = dynamic_cast<derived*> (bptr[1]); if(is_derived) // derived class is_derived -> print(); // call derived functions else // is_derived is NULL; base class bptr[1] -> print(); // call base functions return 0; } bptr[0] is a base object. Derived object COMP152 48

  49. Virtual ? Constructors/Destructors COMP152 49

  50. Constructors cannot be virtual Destructors can be virtual • Usually they should be virtual to have ‘polymorphic’ behavior COMP152 50

More Related