Contents
Polymorphism
The word means to take on many forms. If we have a base class and a derived class that have methods of the same name: say a base class "Person" and a derived class called "Employee". We know that an "Employee" is a "Person" so we can upcast a pointer of "Employee" to "Person" but now if we call the common method "getName" what class method should be called; the "Person" or the "Employee".Polymorphism allows the right ( original ) class method to be called.
File: v1.cpp
#include <iostream> using namespace std ; class Person { public: string firstName ; string lastName ; virtual void getName() { cout << firstName << " " << lastName << endl ; } }; class Employee : public Person { public: string jobTitle ; //virtual void getName() { cout << firstName << " " << lastName << " " << jobTitle << endl ; } }; int main() { Person* p1 ; Employee* e1 ; e1 = new Employee() ; e1->firstName = "Chuck" ; e1->lastName = "Wepner" ; e1->jobTitle = "Boxer" ; p1 = e1 ; p1->getName() ; delete e1 ; return 0 ; }
In the above code the "Employee" pointer is upcast to the base class pointer( "Person" ). The pointer then invokes "getName()" and this function is defined in both the base and derived classes so which one gets called ? This is where Polymorphism comes in.
The poymorphism does not work with objects but only pointers and references.
1) Virtual functions cannot be static. 2) A virtual function can be a friend function of another class. 3) Virtual functions should be accessed using a pointer or reference of base class type to achieve runtime polymorphism. 4) The prototype of virtual functions should be the same in the base as well as the derived class. 5) They are always defined in the base class and overridden in a derived class. It is not mandatory for the derived class to override (or re-define the virtual function), in that case, the base class version of the function is used. 6) A class may have a virtual destructor but it cannot have a virtual constructor.Virtual methods are implemented by virtual method tables. Each class will keep a virtual method table keeping pointers to the functions. https://pabloariasal.github.io/2017/06/10/understanding-virtual-tables/ If we don't want Polymorphism then we don't use the "virtual" keyword. This saves us resources. Polymorphism begins in the hierarchy when the virtual keyword is declared.
If we have say three classes A method M1 B virtual method M1 C virtual method M1 Then if we create an object of type C and upcast to A then we will not have polymorphism. However if we type cast to B then we will have polymorphism. Also once we write the virtual keyword then we do not need to write the virtual keyword in the dervied classes methods.
File: v2.cpp
#include <iostream> using namespace std ; class Person { public: string firstName ; string lastName ; //virtual void getName() { cout << firstName << " " << lastName << endl ; } }; class Employee : public Person { public: string jobTitle ; //Polymorphism starts from here virtual void getName() { cout << firstName << " " << lastName << " " << jobTitle << endl ; } }; class Manager : public Employee { public: //ok to leave virtual out virtual void getName() { cout << firstName << " " << lastName << " " << jobTitle << " Manager" << endl ; } }; int main() { Person* p1 ; Employee* e1 ; Manager* m1 ; e1 = new Employee() ; e1->firstName = "Chuck" ; e1->lastName = "Wepner" ; e1->jobTitle = "Boxer" ; p1 = e1 ; p1->getName() ; delete e1 ; m1 = new Manager() ; m1->firstName = "Chuck" ; m1->lastName = "Wepner" ; m1->jobTitle = "Boxer" ; e1 = m1 ; e1->getName() ; //Make sure we delete the right type //or program will crash delete m1 ; return 0 ; }If we have the actual object but that object does not have the virtual method defined then we climb up the hierarchy till we find a method definition.
File: v3.cpp
#include <iostream> using namespace std ; class Person { public: string firstName ; string lastName ; virtual void getName() { cout << firstName << " " << lastName << endl ; } }; class Employee : public Person { public: string jobTitle ; virtual void getName() { cout << firstName << " " << lastName << " " << jobTitle << endl ; } }; class Manager : public Employee { public: //What if the method is missing //Climb up the hierarchy /* virtual void getName() { cout << firstName << " " << lastName << " " << jobTitle << " Manager" << endl ; } */ }; int main() { Person* p1 ; Employee* e1 ; Manager* m1 ; m1 = new Manager() ; m1->firstName = "Chuck" ; m1->lastName = "Wepner" ; m1->jobTitle = "Boxer" ; p1 = m1 ; p1->getName() ; //Make sure we delete the right type //if we don't have virtual destructors delete m1 ; return 0 ; }
Virtual Destructor
Anytime we have destructors in our hierarchy and use polymorphism we should make the destructors virtual. That way when we have a pointer to the base class and we do a delete then the derived destructor also gets called.File: v4.cpp
#include <iostream> using namespace std ; //Virtual Destructor class Person { public: string firstName ; string lastName ; //virtual void getName() { cout << firstName << " " << lastName << endl ; } Person() { cout << "Person Constructor." << endl ; } virtual ~Person() { cout << "Person Destructor." << endl ; } }; class Employee : public Person { public: string jobTitle ; virtual void getName() { cout << firstName << " " << lastName << " " << jobTitle << endl ; } Employee() { cout << "Employee Constructor." << endl ; } //virtual ~Employee() { cout << "Employee Destructor." << endl ; } }; class Manager : public Employee { public: //What if the method is missing //Climb up the hierarchy virtual void getName() { cout << firstName << " " << lastName << " " << jobTitle << " Manager" << endl ; } Manager() { cout << "Manager Constructor." << endl ; } //virtual ~Manager() { cout << "Manager Destructor." << endl ; } }; int main() { Person* p1 ; Employee* e1 ; Manager* m1 ; e1 = new Employee() ; e1->firstName = "Chuck" ; e1->lastName = "Wepner" ; e1->jobTitle = "Programmer" ; p1 = e1 ; p1->getName() ; delete p1 ; cout << endl << endl << endl ; m1 = new Manager() ; m1->firstName = "Chuck" ; m1->lastName = "Wepner" ; m1->jobTitle = "Programmer" ; p1 = m1 ; e1->getName() ; delete p1 ; return 0 ; }
Output is: Person Constructor. Employee Constructor. Employee Destructor. Person Destructor. Person Constructor. Employee Constructor. Manager Constructor. Manager Destructor. Employee Destructor. Person Destructor.If we don't have the destructor as virtual then we get the following results. We may get the right results or not. It is always better to use the virtual keyword for destructors.
File: v5.cpp
#include <iostream> using namespace std ; //Virtual Destructor //We need to make it virtual class Person { public: string firstName ; string lastName ; virtual void getName() { cout << firstName << " " << lastName << endl ; } Person() { cout << "Person Constructor." << endl ; } ~Person() { cout << "Person Destructor." << endl ; } }; class Employee : public Person { public: string jobTitle ; virtual void getName() { cout << firstName << " " << lastName << " " << jobTitle << endl ; } Employee() { cout << "Employee Constructor." << endl ; } ~Employee() { cout << "Employee Destructor." << endl ; } }; class Manager : public Employee { public: //What if the method is missing //Climb up the hierarchy virtual void getName() { cout << firstName << " " << lastName << " " << jobTitle << " Manager" << endl ; } Manager() { cout << "Manager Constructor." << endl ; } ~Manager() { cout << "Manager Destructor." << endl ; } }; int main() { Person* p1 ; Employee* e1 ; Manager* m1 ; e1 = new Employee() ; e1->firstName = "Chuck" ; e1->lastName = "Wepner" ; e1->jobTitle = "Boxer" ; p1 = e1 ; p1->getName() ; delete e1 ; cout << endl << endl ; m1 = new Manager() ; m1->firstName = "Chuck" ; m1->lastName = "Wepner" ; m1->jobTitle = "Boxer" ; p1 = m1 ; e1->getName() ; delete p1 ; return 0 ; }
Output is: Person Constructor. Employee Constructor. Employee Destructor. Person Destructor. Person Constructor. Employee Constructor. Manager Constructor. Manager Destructor. Employee Destructor. Person Destructor.We can have a private virtual method in the base calss and it can only be called by the base class method but polymorphism will still occur.
File: v6.cpp
#include <iostream> using namespace std ; //Virtual Destructor //We need to make it virtual class Person { public: string firstName ; string lastName ; Person() { cout << "Person Constructor." << endl ; } ~Person() { cout << "Person Destructor." << endl ; } void print() { getName() ; } private: virtual void getName() { cout << firstName << " " << lastName << endl ; } }; class Employee : public Person { public: string jobTitle ; virtual void getName() { cout << firstName << " " << lastName << " " << jobTitle << endl ; } Employee() { cout << "Employee Constructor." << endl ; jobTitle = "Employee title." ; } ~Employee() { cout << "Employee Destructor." << endl ; } }; class Manager : public Employee { public: //What if the method is missing //Climb up the hierarchy virtual void getName() { cout << firstName << " " << lastName << " " << jobTitle << " Manager" << endl ; } Manager() { cout << "Manager Constructor." << endl ; } ~Manager() { cout << "Manager Destructor." << endl ; } }; int main() { Person* p1 ; Employee* e1 ; Manager* m1 ; e1 = new Employee() ; e1->firstName = "Chuck" ; e1->lastName = "Wepner" ; e1->jobTitle = "Programmer" ; p1 = e1 ; p1->print() ; delete e1 ; /* cout << endl << endl ; m1 = new Manager() ; m1->firstName = "Chuck" ; m1->lastName = "Wepner" ; m1->jobTitle = "Manager" ; p1 = m1 ; e1->getName() ; delete m1 ; */ return 0 ; }There is also the case of what happens when a virtual method calls a non-virtual method. We have upcasted a derived pointer to a base class pointer and call a virtual method that is implemented in the derived class. Assume we have a non-virtual method implemented in both the classes and now the virtual method in the derived calls this non-virtual method. Which method is going to be called ? In this case the non-virtual method in the derived class will be called.
File: v7.cpp
#include <iostream> using namespace std ; //Virtual Destructor //We need to make it virtual class Person { public: string firstName ; string lastName ; virtual void getName() { cout << firstName << " " << lastName << endl ; } void print1() { cout << "Person::print1()" << endl ; } Person() { cout << "Person Constructor." << endl ; } ~Person() { cout << "Person Destructor." << endl ; } }; class Employee : public Person { public: string jobTitle ; virtual void getName() { cout << firstName << " " << lastName << " " << jobTitle << endl ; print1() ; } void print1() { cout << "Employee::print1()" << endl ; } Employee() { cout << "Employee Constructor." << endl ; } ~Employee() { cout << "Employee Destructor." << endl ; } }; class Manager : public Employee { public: //What if the method is missing //Climb up the hierarchy virtual void getName() { cout << firstName << " " << lastName << " " << jobTitle << " Manager" << endl ; } Manager() { cout << "Manager Constructor." << endl ; } ~Manager() { cout << "Manager Destructor." << endl ; } }; int main() { Person* p1 ; Employee* e1 ; Manager* m1 ; e1 = new Employee() ; e1->firstName = "Chuck" ; e1->lastName = "Wepner" ; e1->jobTitle = "Programmer" ; p1 = e1 ; p1->print1() ; p1->getName() ; delete e1 ; return 0 ; } $ g++ v7.cpp ; ./a.exe Person Constructor. Employee Constructor. Person::print1() Chuck Wepner Programmer Employee::print1() Employee Destructor. Person Destructor.