Virtual Functions
Posted on April 20, 2023 • 4 minutes • 677 words
In C++, a virtual function is a member function of a class that can be overridden in a derived class. When a virtual function is called on a derived class object, the function of the derived class is called instead of the function of the base class. This is known as dynamic binding or runtime polymorphism.
Syntax:
virtual return_type functionName()
{
//body of the function
return statement;
}
Suppose we have a base class named Shape and three derived classes named Circle and Rectangle and Triangle. And we have a function called drawShape() that simply prints out “Drawing a [shape_name]”. So we can pass a pointers of each derived classes to the function and simply use the getName of the object. And also print the area of the given shape pointer.
First we implement as normal function and see what will be the output.
Base Class:
class Shape
{
public:
double getArea(){
return 0.0;
}
std::string getName(){
return "Shape";
}
};
Child Classes:
For now we consider only one child class called Circle.
class Circle : public Shape
{
public:
Circle(double rad){
radius = rad;
}
double getArea(){
return (3.14 * radius * radius);
}
std::string getName(){
return "Circle";
}
private:
double radius;
};
Main function:
In the main function we create the pointer of the Circle class and assign to Shape type;
Then we try to call the getArea()
function. This will lead to a unexpected result.
int main(int argc, char const *argv[])
{
Circle *cPtr = new Circle(10);
std::cout << "Area: " << cPtr->getArea() << std::endl;
std::cout << "Name: " << cPtr->getName() << std::endl;
delete cPtr;
return 0;
}
Output:
Area: 0.0
Name: Shape
But we expected area is 314
and Name is Circle
This is because the compiler only knows about the base class’s members and does not know about the child class’s members at compile-time. The actual function that is called depends on the type of object the pointer is pointing to at runtime.
To call the child class’s function, you can either cast the pointer to the child class type, or you can declare the member function as virtual in the base class. When a function is declared as virtual in the base class, the appropriate function of the child class will be called at runtime, regardless of the type of pointer used to call it. This is known as runtime polymorphism, and it allows you to write more flexible and extensible code.
So the Base class declaration can be re-write as
class Shape
{
public:
virtual double getArea(){
return 0.0;
}
virtual std::string getName(){
return "Shape";
}
};
Then our result will be
Area: 314
Name: Circle
Full Code
#include <iostream>
#include <cmath>
class Shape
{
public:
virtual double getArea(){
return 0.0;
}
double getPerimeter(){
return 0.0;
}
virtual std::string getName(){
return "Shape";
}
};
class Circle : public Shape
{
public:
Circle(double rad){
radius = rad;
}
double getArea(){
return (3.14 * radius * radius);
}
double getPerimeter(){
return (2 * 3.14 * radius);
}
std::string getName(){
return "Circle";
}
private:
double radius;
};
class Rectangle : public Shape
{
public:
Rectangle(double len, double wid){
length = len;
width = wid;
}
double getArea(){
return (length * width);
}
double getPerimeter(){
return (2 * (length + width));
}
std::string getName(){
return "Rectangle";
}
private:
double length, width;
};
class Triangle : public Shape
{
public:
Triangle(double s1, double s2, double s3){
side1 = s1;
side2 = s2;
side3 = s3;
}
double getArea(){
double s = (side1 + side2 + side3) / 2;
double area = sqrt(s * (s-side1) * (s-side2) * (s-side3));
return area;
}
double getPerimeter(){
return (side1 + side2 + side3);
}
std::string getName(){
return "Triangle";
}
private:
double side1, side2, side3;
};
void DrawShape(Shape *sPtr)
{
std::cout<< sPtr->getName() << " Drawing.....!" << std::endl;
std::cout << "Area : " << sPtr->getArea() << std::endl;
}
int main(int argc, char const *argv[])
{
Circle *cPtr = new Circle(10);
Rectangle *rPtr = new Rectangle(20,10);
Triangle *tPtr = new Triangle(4,5,6);
DrawShape(cPtr);
DrawShape(rPtr);
DrawShape(tPtr);
delete cPtr;
delete rPtr;
delete tPtr;
return 0;
}