This article provides a comprehensive overview of polymorphism in C++ programming language. Concepts like virtual functions, pure virtual functions and others are covered with example programs.
Contents
Introduction
Polymorphism is one of the key features of object orientation. It means many forms or single interface multiple implementations. Polymorphism is of two types: 1) Compile-time polymorphism and 2) Run-time polymorphism as illustrated in the following figure:
Function Overriding
When a base class and sub class contains a function with the same signature, and the function is called with base class object, then the function in derived class executes and the function in base class is said to be overridden. This is known as function overriding.
Following program demonstrates function overriding:
#include<iostream>
using namespace std;
class A
{
protected:
int x;
public:
void show()
{
cout<<"x = "<<x<<endl;
}
};
class B : public A
{
protected:
int y;
public:
B(int x, int y)
{
this->x = x;
this->y = y;
}
void show()
{
cout<<"x = "<<x<<endl;
cout<<"y = "<<y<<endl;
}
};
int main()
{
A objA;
B objB(30, 20);
objB.show();
return 0;
}
Output for the above program is:
x = 30
y = 20
In the above program, both super class A and sub class B contains the same function show() with same signature (function name plus parameters). So, when sub class object is used to call show(), function in sub class B executes overriding the function in super class A.
Note: Overloading of functions is not supported across classes (in inheritance) in C++.
Virtual Functions
We know that when a base class pointer refers to a derived class object, the extra features in derived class are not available. To access the extra features in the derived class, we make the functions in the base class as virtual. Syntax for creating a virtual function is as follows:
virtual return-type function-name(params-list)
{
//Body of function
...
}
A class which contains one or more virtual functions is known as a polymorphic class. Following program demonstrates accessing derived class features using virtual functions:
#include<iostream>
using namespace std;
class A
{
protected:
int x;
public:
virtual void show() //virtual function
{
cout<<"x = "<<x<<endl;
}
};
class B : public A
{
protected:
int y;
public:
B(int x, int y)
{
this->x = x;
this->y = y;
}
void show()
{
cout<<"x = "<<x<<endl;
cout<<"y = "<<y<<endl;
}
};
int main()
{
A *bptr;
B objB(30, 20);
bptr = &objB;
bptr->show();
return 0;
}
Output of the above program is as follows:
x = 30
y = 20
Rules for Virtual Functions
Following points must be remembered while working with virtual functions:
- Virtual functions must be members of a class.
- Virtual functions must be created in public section so that objects can access them.
- When virtual function is defined outside the class, virtual keyword is required only in the function declaration. Not necessary in the function definition.
- Virtual functions cannot be static members.
- Virtual functions must be accessed using a pointer to the object.
- A virtual function cannot be declared as a friend of another class.
- Virtual functions must be defined in the base class even though it does not have any significance.
- The signature of virtual function in base class and derived class must be same.
- A class must have a virtual destructor but it cannot have a virtual constructor.
Pure Virtual Functions
When the code for virtual function in a base class is insignificant, we can make such virtual functions as pure virtual functions. A pure virtual function is a virtual function without any definition. Syntax for creating a pure virtual functions is as follows:
virtual return-type function-name(params-list) = 0;
A class which contains at least one pure virtual function is called a abstract class and the class which provides the definition for the pure virtual function is called a concrete class.
Late Binding (Dynamic Polymorphism)
In inheritance when a derived class object is assigned to a base class pointer, and a polymorphic function is invoked, the function call is linked to the function definition at run-time. Such postponement of linkage to run-time instead of compile-time is called as late binding or dynamic polymorphism.
Abstract Class
A class which contains at least one pure virtual function is called an abstract class. Since abstract class is incomplete, another class should derive it and provide definitions for the pure virtual functions in the abstract class.
Following are the features of an abstract class:
- Abstract class must contain at least one pure virtual function.
- Objects cannot be created for an abstract class. But pointers can be created.
- Classes inheriting an abstract class must provide definitions for all pure virtual functions in the abstract class. Otherwise, the sub class also becomes an abstract class.
- An abstract class can have non-virtual functions and data members.
Following are the uses of an abstract class:
- Abstract class provides a common standard interface for all the sub classes.
- Abstract classes allow new features to be easily added to an existing application.
Following program demonstrates pure virtual functions, late binding and an abstract class:
#include <iostream>
using namespace std;
class Shape //Abstract class
{
public:
virtual void area() = 0;
virtual void peri() = 0;
};
class Rectangle : public Shape //Concrete class
{
private:
int l;
int b;
public:
Rectangle(int l, int b)
{
this->l = l;
this->b = b;
}
void area()
{
cout<<"Area of rectangle: "<<(l*b)<<endl;
}
void peri()
{
cout<<"Perimeter of rectangle: "<<2*(l+b)<<endl;
}
};
class Circle : public Shape //Concrete class
{
private:
int r;
public:
Circle(int r)
{
this->r = r;
}
void area()
{
cout<<"Area of circle: "<<(3.14*r*r)<<endl;
}
void peri()
{
cout<<"Perimeter of circle: "<<(2*3.14*r)<<endl;
}
};
int main()
{
Shape *s;
Rectangle r(10, 20);
Circle c(4);
s = &r;
s->area(); //late binding
s->peri(); //late binding
s = &c;
s->area(); //late binding
s->peri(); //late binding
return 0;
}
Output for the above program is as follows:
Area of rectangle: 200
Perimeter of rectangle: 60
Area of circle: 50.24
Perimeter of circle: 25.12
Virtual Constructors and Destructors
C++ allows programmers to create virtual destructors. But, it doesn’t allow virtual constructors to be created because of various reasons. To know why a virtual destructor is needed, consider the following program:
#include <iostream>
using namespace std;
class A
{
public:
A()
{
cout<<"A's constructor"<<endl;
}
~A()
{
cout<<"A's destructor"<<endl;
}
};
class B : public A
{
public:
B()
{
cout<<"B's constructor"<<endl;
}
~B()
{
cout<<"B's destructor"<<endl;
}
};
int main()
{
A *bptr = new B();
delete bptr;
return 0;
}
Output for the above program is as follows:
A's constructor
B's constructor
A's destructor
From the above output you can see that derived class destructor didn’t execute. This might lead to problems like memory leakage. To avoid such problems we make destructor in base class as virtual destructor.
Virtual destructor ensures that the derived class destructor is executed. To create a virtual destructor, precede the destructor definition with virtual keyword. Following program demonstrates a virtual destructor:
#include <iostream>
using namespace std;
class A
{
public:
A()
{
cout<<"A's constructor"<<endl;
}
virtual ~A()
{
cout<<"A's destructor"<<endl;
}
};
class B : public A
{
public:
B()
{
cout<<"B's constructor"<<endl;
}
~B()
{
cout<<"B's destructor"<<endl;
}
};
int main()
{
A *bptr = new B();
delete bptr;
return 0;
}
Output for the above program is as follows:
A's constructor
B's constructor
B's destructor
A's destructor
Now you can see that derived class constructor is also executed.
Advantages and Disadvantages of Inheritance
Advantages of inheritance are as follows:
- Inheritance promotes reusability. When a class inherits or derives another class, it can access all the functionality of inherited class.
- Reusability enhanced reliability. The base class code will be already tested and debugged.
- As the existing code is reused, it leads to less development and maintenance costs.
- Inheritance makes the sub classes follow a standard interface.
- Inheritance helps to reduce code redundancy and supports code extensibility.
- Inheritance facilitates creation of class libraries.
Disadvantages of inheritance are as follows:
- Inherited functions work slower than normal function as there is indirection.
- Improper use of inheritance may lead to wrong solutions.
- Often, data members in the base class are left unused which may lead to memory wastage.
- Inheritance increases the coupling between base class and derived class. A change in base class will affect all the child classes.
Suryateja Pericherla, at present is a Research Scholar (full-time Ph.D.) in the Dept. of Computer Science & Systems Engineering at Andhra University, Visakhapatnam. Previously worked as an Associate Professor in the Dept. of CSE at Vishnu Institute of Technology, India.
He has 11+ years of teaching experience and is an individual researcher whose research interests are Cloud Computing, Internet of Things, Computer Security, Network Security and Blockchain.
He is a member of professional societies like IEEE, ACM, CSI and ISCA. He published several research papers which are indexed by SCIE, WoS, Scopus, Springer and others.
Leave a Reply