Polymorphism Polymorphism - is the property of OOP that allows the run-time binding of a function's name to the code that implements the function. (Run-time binding to the starting address of the code.) Compile-time binding of function names: Typically the compiler determines the code to be executed when a function call is made. At compile time the starting address of the code segment of the function called is inserted where the call occurs. ; class DC: public BC ; void BC::f1() void DC::f1() cout << "DC:: f1() called"<<endl; BC* ptr; int sel; do cout << " Which one? 1-BC, 2-DC, other - quit "; cin >> sel; if (sel ==1) ptr = new BC; else if (sel==2) ptr = new DC; else cout << "bye" << endl; if ((sel ==1) (sel == 2)) ptr->f1(); delete ptr; while ((sel==1) (sel ==2)); Output: Which one are you? 1-BC, 2-DC, other - quit 1 Which one are you? 1-BC, 2-DC, other - quit 2 Which one are you? 1-BC, 2-DC, other - quit 3 bye 08/25/18 1
Run-time binding of function names: The starting address of the function is determined during program execution. Some OOP languages only use run-time binding of functions - Smalltalk. Using same example insert virtual in front of BC declaration of f1(): virtual ; Output: Which one are you? 1-BC, 2-DC, other - quit 1 Which one are you? 1-BC, 2-DC, other - quit 2 DC:: f1() called Which one are you? 1-BC, 2-DC, other - quit 3 bye C++ Polymorphism Requirements: There must be an inheritance hierarchy The classes in the hierarchy must have a virtual method with the same signature (virtual is a keyword for method declaration only). If the base class has the method declared virtual then any derived class method of the same signature is automatically virtual. There must be either a pointer or a reference to a base class. The pointer or reference is used to invoke a virtual method. (Note: declaring a pointer to point to the base class data type allows it to also point to any derived class object.) Virtual table (vtable) - The table used for the run-time binding of virtual methods. A table exists for every virtual method. During execution it is determined which object a pointer points. This is used to lookup in the vtable the entry point of the method. The method is then executed. (Performance penalty and storage penalty) Constructors - Cannot be virtual. Proper constructors are always called because they are based on the object created. Destructors - Should be virtual. Compile bound destructors are based on the pointer declaration and will usually only call the base class destructor. Run-time binding leaves the destructor to run-time and then is based on the object. 08/25/18 2
BC(); ~BC(); ; class DC: public BC DC(); ~DC(); ; BC* ptr; int sel; do cout << endl << "========="<< endl; cout << " Which one? 1-BC, 2-DC, other - quit "; cin >> sel; if (sel ==1) ptr = new BC; else if (sel==2) ptr = new DC; else cout << "bye" << endl; BC::BC() cout <<"BC::Constr called" << endl; BC::~BC() cout <<"BC::DEstr called" << endl; if ((sel ==1) (sel == 2)) ptr->f1(); delete ptr; while ((sel==1) (sel ==2)); void BC::f1() DC::DC() cout <<"DC::Constr called" << endl; DC::~DC() cout <<"DC::DEstr called" << endl; void DC::f1() cout << "DC:: f1() called"<<endl; Output: ===================================== Which one are you? 1-BC, 2-DC, other - quit 1 BC::Constructor called BC::DEstructor called ==================================== Which one are you? 1-BC, 2-DC, other - quit 2 BC::Constructor called DC::Constructor called BC::DEstructor called ==================================== Which one are you? 1-BC, 2-DC, other - quit 3 bye 08/25/18 3
Making the destructor virtual solves the problem: BC(); virtual ~BC(); virtual ; Output: Which one are you? 1-BC, 2-DC, other - quit 1 BC::Constructor called BC::DEstructor called Which one are you? 1-BC, 2-DC, other - quit 2 BC::Constructor called DC::Constructor called DC:: f1() called DC::DEstructor called BC::DEstructor called Which one are you? 1-BC, 2-DC, other - quit 3 bye Press any key to continue 08/25/18 4
Name Overloading - functions or methods with the same name but different signatures. Compiler can distinguish which function start address to bind the call to. Example: a class with multiple constructors Example: void f1( int x); void f1(char c); Name Overriding - Methods with the same name and same signature. If the methods are virtual then run-time binding takes place. If they are not virtual, compile-time binding is at work. ; class DC: public BC ; BC* ptr; ptr = new DC; ptr->f1(); return 0; void BC::f1() void DC::f1() cout << "DC:: f1() called"<<endl; OUTPUT: (compile-time binding) BC::f1( ) called OUTPUT: (run-time binding - add virtual to BC::f1() ) DC::f1( ) called 08/25/18 5
Name Hiding - an inherited member has the same name (identifier) as the derived class but a different signature. void f1(int x); ; class DC: public BC ; DC d; d.f1( ); // d.f1(15); this will cause an error d.bc::f1(15); // this is ok return 0; void BC::f1(int x) void DC::f1() cout << "DC:: f1() called"<<endl; OUTPUT: (compile-time binding) DC::f1( ) called 08/25/18 6
Abstract Base Class - Creating an abstraction or basic template for derived classes. A class is considered to be abstract if it has one pure virtual function: virtual datatype function-name( parameters) = 0; virtual void Print( ) = 0; The derived class must override (create a same sign. function) this function to be nonabstract. Abstract classes cannot be instantiated to an object. virtual void f1() = 0; ; class DC: public BC ; // BC b ; ERROR DC d; d.f1( ); return 0; void DC::f1() cout << "DC:: f1() called"<<endl; OUTPUT: (compile-time binding) DC::f1( ) called 08/25/18 7
Run-time type identification - There are several ways to determine the type of an object at run-time. Check type conversion at run-time (dynamic_cast) Determine object's type at run-time (typeid) Extend RTTI (run-time type identification) provided by C++ // polymorphism - dynamic cast #include <typeinfo> virtual ; class DC1: public BC ; class DC2: public BC ; void BC::f1() void DC1::f1() cout << "DC1:: f1() called"<<endl; void DC2::f1() cout << "DC2:: f1() called"<<endl; 08/25/18 8
BC* ptr; DC1* p; int sel; do cout << endl << "==========================================" << endl; cout << " Which one are you? 1-DC1, 2-DC2, 3-BC, other - quit "; cin >> sel; if (sel ==1) ptr = new DC1; else if (sel==2) ptr = new DC2; else if (sel ==3) ptr = new BC; else cout << "bye" << endl; if ((sel ==1) (sel == 2) (sel == 3)) ptr->f1(); p = dynamic_cast<dc1 *> (ptr); if (p) cout << "DC1 CLASS" << endl; delete ptr; while ((sel==1) (sel ==2) (sel == 3)); return 0; 08/25/18 9
// polymorphism - typeid #include <typeinfo> virtual ; class DC1: public BC ; class DC2: public BC ; void BC::f1() void DC1::f1() cout << "DC1:: f1() called"<<endl; void DC2::f1() cout << "DC2:: f1() called"<<endl; 08/25/18 10
BC* ptr; int sel; do cout << endl << "===========================================" << endl; cout << " Which one are you? 1-DC1, 2-DC2, 3-BC, other - quit "; cin >> sel; if (sel ==1) ptr = new DC1; else if (sel==2) ptr = new DC2; else if (sel ==3) ptr = new BC; else cout << "bye" << endl; if ((sel ==1) (sel == 2) (sel == 3)) ptr->f1(); if (typeid(*ptr) == typeid(bc)) cout << "BASE CLASS" << endl; else if (typeid(*ptr) == typeid(dc1)) cout << "DC1 CLASS" << endl; else if (typeid(*ptr) == typeid(dc2)) cout << "DC2 CLASS" << endl; delete ptr; while ((sel==1) (sel ==2) (sel == 3)); return 0; 08/25/18 11