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.
- Polymorphism: a call to a member function will cause a different function to be executed depending on the type of object that invokes the function
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 = ▭
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 = ▭
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:
- Each of the child classes has a separate implementation for the function (e.g.
area()
) - Different classes with a function of the same name, and even the same parameters, but with different implementations
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
- This is known as a pure virtual function
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