Virtual function
- First we will go through a problem scenario then we will see how to overcome it.
- When show() function is called. Now question is what will be the outcome???
- This program prints "we are in base class yeah", although the pointer was storing the address of derived
class.
- So this was the problem, now this problem can be resolved using "virtual function".
- While covering this problem there are many topics that are also involved in this like- late binding, we
will also cover them.
Virtual function
- A virtual function is a member function in a base class that can be overridden by a derived class to
provide a different implementation. It allows the derived class to define its own version of the
function, providing flexibility and extensibility in the object-oriented hierarchy.
- The virtual keyword is used to declare a function as virtual in the base class.
- Virtual functions enable dynamic polymorphism, which means that a function call is resolved
dynamically at runtime based on the actual object type rather than the declared type. This allows
objects of different derived classes to be treated uniformly through a base class pointer or
reference, simplifying code and improving flexibility.
- When a virtual function is called through a base class pointer or reference, the function that is
invoked is determined at runtime based on the type of the actual object being pointed to or
referenced. This is known as dynamic or late binding.
- Virtual functions are typically used in scenarios where different derived classes need to provide
their own implementation of a specific behavior defined in the base class. The base class provides a
common interface through virtual functions, while the derived classes override these functions to
customize their behavior.
- It is important to note that virtual functions should be declared in the base class and overridden
in the derived classes using the override keyword to ensure proper overriding and to indicate the
intention of overriding the base class function.
Let's resolve the problem
#include <iostream>
using namespace std;
class base_class
{
public:
virtual void show()
{
cout << "we are in base class yeah";
}
};
class derived_class : public base_class
{
public:
void show()
{
cout << "we are in derived class yeah";
}
};
int main()
{
base_class *basePtr;
derived_class objOfDerivedC;
basePtr = &objOfDerivedC;
basePtr->show();
return 0;
}
Output ↓
we are in derived class yeah
- Just including 'virtual' in definition of show() function of base class, the problem is resolved.
Early binding & late binding
Early binding (static binding)
- Early binding occurs when the function call is resolved at compile-time.
- The compiler determines which function to call based on the static type of the object. It is
also known as static binding because the binding between the function call and the function
implementation is done before the program is executed.
Program to demonstrate early binding
#include <iostream>
using namespace std;
class Shape
{
public:
void draw()
{
cout << "Drawing Shape." << endl;
}
};
class Circle : public Shape
{
public:
void draw()
{
cout << "Drawing Circle." << endl;
}
};
int main()
{
Shape shape;
Circle circle;
Shape *shapePtr = &shape;
shapePtr->draw(); // Calls Shape's draw() function (early binding)
shapePtr = &circle;
shapePtr->draw(); // Calls Shape's draw() function (early binding)
return 0;
}
- Just because we are not using virutal function this becomes example of early binding.
Late binding (Dynamic binding)
- Late binding occurs when the function call is resolved at runtime. The compiler defers the
binding of the function call to the actual object until runtime. It is also known as dynamic
binding or virtual binding.
#include <iostream>
using namespace std;
class Shape
{
public:
virtual void draw()
{
cout << "Drawing Shape." << endl;
}
};
class Circle : public Shape
{
public:
void draw()
{
cout << "Drawing Circle." << endl;
}
};
int main()
{
Shape *shapePtr;
Shape shape;
Circle circle;
shapePtr = &shape;
shapePtr->draw(); // Calls Shape's draw() function (late binding)
shapePtr = &circle;
shapePtr->draw(); // Calls Circle's draw() function (late binding)
return 0;
}
Pure virtual functions
Example ↓
#include <iostream>
using namespace std;
class Shape
{
public:
virtual void draw() = 0; // Pure virtual function
};
class Circle : public Shape
{
public:
void draw() override // overide is keyword which ensure we are overriding function in derived class from base class.
{
cout << "Drawing Circle." << endl;
}
};
class Rectangle : public Shape
{
public:
void draw() override
{
cout << "Drawing Rectangle." << endl;
}
};
int main()
{
Circle circle;
Rectangle rectangle;
circle.draw(); // Calls Circle's draw() function
rectangle.draw(); // Calls Rectangle's draw() function
return 0;
}
Abstract classes
- An abstract class is a class that contains at least one pure virtual function.
- Abstract classes cannot be instantiated directly; they serve as base classes for derived classes.
- Derived classes must provide implementations for all pure virtual functions in the abstract base
class.
Example program ↓
#include <iostream>
using namespace std;
class Shape // abstract class
{
public:
virtual void draw() = 0; // Pure virtual function
};
class Circle : public Shape
{
public:
void draw() override
{
cout << "Drawing Circle." << endl;
}
};
class Rectangle : public Shape
{
public:
void draw() override
{
cout << "Drawing Rectangle." << endl;
}
};
int main()
{
Circle circle;
Rectangle rectangle;
circle.draw();
rectangle.draw();
return 0;
}
How Runtime polymorphism and Compile-time polymorphism is related to early and late binding??
Runtime polymorphism
- Runtime polymorphism, also known as dynamic polymorphism, is achieved through the use of virtual
functions.
- It allows the selection of the appropriate function implementation at runtime based on the
actual object type. This is possible because virtual functions are resolved dynamically (late
binding).
- The binding of the function call to its implementation occurs at runtime, depending on the type
of the object being referenced or pointed to.
Compile-time Polymorphism
- Compile-time polymorphism, also known as static polymorphism, is achieved through function
overloading and templates. In function overloading, multiple functions with the same name but
different parameter lists can be defined. The appropriate function is selected based on the
static types of the arguments at compile time.
- So, late binding is associated with runtime polymorphism, where the appropriate function
implementation is determined at runtime based on the actual object type. Early binding is associated
with compile-time polymorphism, where the function call is resolved at compile time based on the
static types of the objects involved.