Unit III
Unit III
Polymorphism
Polymorphism means "many forms", it describes the concept that you can access objects of
different types through the same interface.
Early Binding (compile-time time polymorphism) As the name indicates, compiler (or
linker) directly associate an address to the function call. Binding at compile time is known
as static binding.
there are two ways by which static binding can be achieved: function overloading & operator
overloading. Overloading is a technique through which the compiler uniquely identifies each
function definition when it is called or linked. It locates by matching the parameters passed to
those functions.
There are some known advantages associated with static binding in C++ :
It performs better than a dynamic binding in C++. The compiler knows before runtime about
all the methods of the class & is aware of which ones can or can’t be overridden. Hence, it is
easier for the compiler to associate the objects with their respective class(es). It concludes
with not needing an extra overhead.
Better performance also results in being more efficient and faster.
There is a downside to the static binding. It reduces flexibility as all information about the
values of parameters & function calling is predefined and cannot be changed at runtime.
Now that we are grasping some concepts of static binding let us look at an example to get
through it.
Examples of Static Binding
We have seen that static binding in C++ is implemented using function
overloading & operator overloading. Hence, let’s start with static binding using function
overloading.
Function Overloading
When two or more functions have the same name but differ in the types or number of
parameters they accept, it is known as function overloading. It is a feature
of object-oriented programming.
Now let us create a class with member functions only to depict static binding in C++. We will
return a different number of parameters to overload our member functions.
#include <iostream>
Note: First, we called the function with three parameters on purpose to check if the function
calls & definitions are linked correctly or not.
Operator Overloading
Usually, we use operators to do mathematical calculations, but if we want to do operations on
the objects of a class, how can that be performed?
It takes place using operator overloading. In operator overloading, we give a special
meaning to our operator to perform operations on objects of a class. Hence, we overload our
desired operator.
We can perform unary operator overloading that works on one operand only or binary
operator overloading that works on two operands. For this example, we will consider binary
operator overloading.
Let us try to add two numbers using a class. The class definition with the driver code is as
follows:
#include <iostream> cout << " Enter the second
using namespace std; number(operand): ";
class Over_num { cin >> x;
int x, y; }
public:
void input(); // Display the result of the addition.
void input2(); void Over_num::print() {
// Overloading the binary '+' operator to cout << "The sum of two numbers is: "
add number. << x;
Over_num operator + (Over_num & }
ob);
void print(); // Overload the binary (+) operator.
}; Over_num Over_num::operator +
(Over_num & ob) {
// Member functions to take inputs of two
numbers. Over_num A;
void Over_num::input() { A.x = x + ob.x;
cout << " Enter the first return (A);
number(operand): "; }
cin >> x;
} int main() {
void Over_num::input2() { // Create object of class Over_num i.e
x1 and y1.
Over_num x1, y1, res; // Call the print function to display the
cout << endl; result of the addition.
//Input the values res.print();
x1.input(); cout << endl;
y1.input2();
return 0;
// Addition of the objects. }
res = x1 + y1;
In the given class, we have provided an uncommon meaning to our addition symbol (+). By
overloading it, we will operate on two objects of a class, something unusual in basic
mathematics.
● The code associated with the procedure is not known until the program is executed,
which is also known as late binding.
● Late binding is achieved with the help of virtual keyword
● Late Binding : (Run time polymorphism) In this, the compiler adds code that
identifies the kind of object at runtime then matches the call with the right function
definition achieved by virtual function
● It is used to tell the compiler to perform dynamic linkage or late binding on the
function.
● There is a necessity to use the single pointer to refer to all the objects of the different
classes. So, we create the pointer to the base class that refers to all the derived objects.
But, when base class pointer contains the address of the derived class object, always
executes the base class function. This issue can only be resolved by using the 'virtual'
function.
● A 'virtual' is a keyword preceding the normal declaration of a function.
● When the function is made virtual, C++ determines which function is to be invoked at
the runtime based on the type of the object pointed by the base class pointer.
#include <iostream> {
using namespace std; cout << "Derived Class is invoked"<<end
class A l;
{ }
public: };
virtual void display() int main()
{ {
cout << "Base class is invoked"<<endl; A* a; //pointer of base class
B b; //object of derived class
} a = &b;
}; a->display(); //Late Binding occurs
class B:public A }
{ Output:
public: Derived Class is invoked
void display()
● A virtual function is not used for performing any task. It only serves as a placeholder.
● When the function has no definition, such function is known as "do-nothing" function.
● The "do-nothing" function is known as a pure virtual function. A pure virtual function
is a function declared in the base class that has no definition relative to the base class.
● A class containing the pure virtual function cannot be used to declare the objects of its
own, such classes are known as abstract base classes.
● The main objective of the base class is to provide the traits to the derived classes and
to create the base pointer used for achieving the runtime polymorphism.
Example }
#include <iostream> };
using namespace std; int main()
class Base {
{ Base *bptr;
public: //Base b;
virtual void show() = 0; Derived d;
}; bptr = &d;
class Derived : public Base bptr->show();
{ return 0;
public: }
void show() Output:
{ Derived class is derived from the base
std::cout << "Derived class is derived class.
from the base class." << std::endl;
In the above example, the base class contains the pure virtual function. Therefore, the base
class is an abstract base class. We cannot create the object of the base class.
Certainly! In an Online Railway Reservation System, polymorphism can be demonstrated through the use of base and derived
classes, along with virtual functions, to handle different types of reservations. Let's create a simplified example using C++
#include <iostream>
#include <string>
#include <vector>
class Reservation {
public:
Reservation(const string& name) : passengerName(name) {}
protected:
string passengerName;
};
public:
: Reservation(name), numberOfTickets(numTickets) {}
Reservation::displayDetails();
private:
int numberOfTickets;
};
// Derived class for special reservations (e.g., senior citizens)
public:
: Reservation(name), numberOfTickets(numTickets) {}
Reservation::displayDetails();
private:
int numberOfTickets;
};
int main() {
vector<Reservation*> reservations;
reservations.push_back(regReservation);
reservations.push_back(specialReservation);
reservation->displayDetails();
// Clean up memory
delete reservation;
return 0;
In this example, we've demonstrated polymorphism by using a base class Reservation with virtual functions displayDetails()
and calculateFare(). The derived classes RegularReservation and SpecialReservation override these functions with their
own implementations. The usage of polymorphism allows us to store different types of reservations in a container and call their
respective functions using pointers to the base class.
Please note that this is a simplified example for demonstration purposes. In a real-world scenario, an Online Railway Reservation
System would involve more complex logic and structures.
Upcasting and downcasting are terms used in object-oriented programming to refer to the conversion of pointers or references to
base class types and derived class types, respectively. These conversions are often necessary when working with inheritance
hierarchies. Let's delve into both concepts with examples in C++:
Upcasting:
Upcasting involves converting a pointer or reference of a derived class type to a pointer or reference of its base class type. This
conversion is safe because a derived class object is a specialization of the base class.
#include <iostream>
class Base {
public:
void show() {
};
public:
void show() {
};
int main() {
Derived derivedObj;
return 0;
In this example, the basePtr is a pointer to the base class Base, but it's pointing to an object of the derived class Derived. Despite
this, the basePtr->show() call invokes the show() method of the base class.
Downcasting:
Downcasting involves converting a pointer or reference of a base class type to a pointer or reference of its derived class type. This
is more complex and potentially unsafe since the base class pointer might not actually point to a derived class object.
In C++, you can use dynamic_cast for safe downcasting, which returns a valid pointer to the derived class if the base class pointer
indeed points to an object of the derived class. If not, it returns a nullptr.
#include <iostream>
public:
};
public:
};
int main() {
if (derivedPtr) {
} else {
delete basePtr;
return 0;
In this example, the basePtr is initially pointing to a Derived object. Using dynamic_cast, we attempt to downcast it to a Derived
pointer. If successful, we can call derivedPtr->show() to invoke the derived class's show() method.
Keep in mind that downcasting should be used with caution and is often an indicator of a design issue. It's generally preferred to
use virtual functions and polymorphism to avoid the need for downcasting.
Pointers to objects are variables that store memory addresses of objects rather than the objects themselves. They are frequently
used in programming to work with objects dynamically, create dynamic objects on the heap, and manipulate objects through
references. Here's an example in C++ to illustrate pointers to objects:
#include <iostream>
class MyClass {
public:
int value;
MyClass(int v) : value(v) {}
void display() {
};
int main() {
MyClass obj1(10);
objPtr->display();
heapObjPtr->value = 30;
cout << "Accessing object's member on the heap: " << heapObjPtr->value << endl;
delete heapObjPtr;
return 0;
In this example:
● We define a MyClass class with a member variable value and a method display().
● In the main() function:
● We create an object obj1 of MyClass and then create a pointer objPtr that points to it.
● We demonstrate accessing the object's member (value) and calling the method (display()) through the pointer.
● We create an object on the heap using the new keyword and assign its pointer to heapObjPtr.
● We access and modify the object's member on the heap using the pointer.
● We properly release the memory occupied by the heap object using delete.
Pointers to objects are valuable when working with dynamic memory allocation, polymorphism, and creating arrays of objects,
among other scenarios. They allow for more flexible object manipulation and memory management.
The this pointer is a special pointer available in object-oriented programming languages like C++ that refers to the object on which
a member function is called. It's an implicit parameter passed to the member functions of a class and is used to access the object's
attributes and methods from within those functions. The this pointer provides a way to distinguish between object attributes and
function parameters with the same names.
#include <iostream>
using namespace std;
class MyClass {
public:
int value;
MyClass(int v) : value(v) {}
void display() {
this->value = newValue;
};
int main() {
MyClass obj(42);
return 0;
}
In this example:
● The display() method uses the this pointer to access the value attribute of the object on which the method is called.
● The setValue() method takes an argument newValue and uses the this pointer to modify the value attribute of the calling object.
● In the main() function, we create an object obj of MyClass, call its methods, and demonstrate the use of the this pointer.
The this pointer is automatically available within non-static member functions of a class. It points to the memory location of the
object for which the member function is invoked. This allows you to access the object's attributes and methods without ambiguity,
even if the function parameters have the same names.
Keep in mind that you don't need to explicitly use this-> in most cases; you can access member variables directly. The this
pointer becomes more crucial when you want to differentiate between member variables and function parameters that share the
same name.
Data conversion refers to the process of converting one type or format of data into another. This is a common operation in
programming and computing, where different data types, representations, or formats need to be converted to perform operations,
comparisons, or display in various contexts. Data conversion can involve changing numerical values, text, binary representations,
and more.
1. Implicit Conversion (Type Casting): Implicit conversion, also known as type casting, is an automatic conversion performed by the
compiler when a value of one data type is assigned to a variable of another compatible data type. It generally involves widening
the data type to prevent loss of information.
#include <iostream>
// Abstract class
class Shape {
public:
};
public:
Circle(double r) : radius(r) {}
cout << "Circle Area: " << 3.14159 * radius * radius << endl;
private:
double radius;
};
// Concrete subclass Rectangle
public:
cout << "Rectangle Area: " << length * width << endl;
private:
double length;
double width;
};
int main() {
Circle circle(5);
// Using polymorphism
Shape* shapePtr;
shapePtr = &circle;
shapePtr = &rectangle;
return 0;
}
In this example:
Abstract classes are used to define a common interface or behavior that multiple related classes should follow. They play a crucial
role in achieving a well-organized and extensible design in object-oriented programming.