Home C++ Introduction Decisions Loops Input/Output Functions Stack and Heap References Arrays Searching and Sorting Recursion Pointers Character and Strings Structures Classes Inheritance Exceptions Templatess STL Modern C++ Misc Books ----

Inheritance


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.