C++ Polymorphism

Key Word(s): C++, Polymorphism




Key: :large_orange_diamond: - Code Example :large_blue_diamond: - Code Exercise :red_circle: - Code Warning
Previous: C++ Inheritence
Next: C++ Templates and Namespaces


Class Polymorphism

One of the key features of class inheritance is that a pointer to a derived class is type-compatible with a pointer to its base class.

Example:

:large_orange_diamond: Pointer to Class: Base Class

#include <iostream>
using namespace std;

class Polygon {
  protected:
    int width, height;
  public:
    void set_values(int a, int b){
      width = a;
      height = b;
    }
};

:large_orange_diamond: Pointer to Class: Derived Classes

class Rectangle: public Polygon {
  public:
    int area(){
      return width*height;
    }
};

class Triangle: public Polygon {
  public:
    int area(){
      return width*height/2;
    }
};

:large_orange_diamond: Pointer to Class: Main

int main () {
  Rectangle rect;
  Triangle trgl;

  Polygon *ppoly1 = &rect;
  Polygon *ppoly2 = &trgl;

  ppoly1->set_values(4,5);
  ppoly2->set_values(4,5);

  cout << "Rectangle area: " << rect.area() << endl;
  cout << "Triangle area: " << trgl.area() << endl;
  return 0;
}

:large_orange_diamond: Pointer to Class: Output

Rectangle area: 20
Traingle area: 10


:red_circle: Non-Virtual Base Class Functions

Consider the following example (three code blocks):

:large_orange_diamond: Non-Virtual Function: Base Class

#include <iostream>
using namespace std;

class Polygon {
  protected:
    int width, height;
  public:
    void set_values(int a, int b){
      width = a;
      height = b;
    }

    // base-class area function
    int area(){
      cout << "Calling Parent class area... " << endl;
      return 0;
    }
};

:large_orange_diamond: Non-Virtual Function: Derived Classes

class Rectangle: public Polygon {
  public:
    int area(){
      cout << "Calling Rectangle class area... " << endl;
      return width*height;
    }
};

class Triangle: public Polygon {
  public:
    int area(){
      cout << "Calling Triangle class area... " << endl;
      return width*height/2;
    }
};

:large_orange_diamond: Non-Virtual Function: Main

int main () {
  Rectangle rect;
  Triangle trgl;

  Polygon *ppoly1 = &rect;
  Polygon *ppoly2 = &trgl;

  ppoly1->set_values(4,5);
  ppoly2->set_values(4,5);

  cout << rect.area() << endl;
  cout << trgl.area() << endl;
  return 0;
}

:red_circle: Non-Virtual Function: Output

Calling Parent class area...
Calling Parent class area...

:red_circle: Be careful though with assigning the same pointer to multiple derived classes! Without the keyword virtual we can get static resolution or static linkage meaning the function call is fixed before the program is executed.


What's the fix?

:large_orange_diamond: Virtual Functions

To fix this behavior, we introduce the keyword **virtual**:

class Polygon {
  protected:
    int width, height;
  public:
    void set_values(int a, int b){
      width = a;
      height = b;
    }

    // virtual base-class area function
    virtual int area(){
      cout << "Calling Parent class area... " << endl;
      return 0;
    }
};

:large_orange_diamond: Fix: Virtual Output

Calling Rectangle class area...
Calling Triangle class area...

Now, the compiler looks at the contents of the pointer rather than its type. Hence the expected area() functions being called from each of the derived-classes.


Polymorphism is generally used in the following way:


Virtual Functions

A virtual function is a function in a base class that is declared using the keyword virtual.
Defining in a base class a virtual function, with another version in a derived class, signals to the compiler not to perform static linkage for this function. This sort of operation is referred to as dynamic linkage or late binding.


Pure Virtual Functions

It is possible to include a virtual function in a base class such that it must be redefined in a derived class, but that has no meaningful definition in the context of the base class.

Suppose we wanted area() to be implemented by every derived class and also prevent the base class from assigning a value of 0 as we saw above:

class Shape {
   protected:
      int width, height;
   public:
      Shape(int a = 0, int b = 0){
         width = a;
         height = b;
      }

      // pure virtual function
      virtual int area() = 0;
};

virtual int area() = 0;, the = 0 signals that the function has no body and must be implemented by any derived class


Abstract Base Classes

Our Polygon or Shape classes are really abstractions, and should only serve as base classes which cannot instantiate objects.
:heavy_exclamation_mark: Classes that contain at least one pure virtual function are known as abstract base classes.

When we include virtual int area() = 0;, we have converted the Shape class into an abstract base class. Thus:

Shape myShapeObj;      // ERROR: Shape is an abstract base class
Shape *myShapePointer; // VALID: Pointers to abstract base classes are advantageous

Next: C++ Templates and Namespaces