0% found this document useful (0 votes)
20 views18 pages

Unit III

The document discusses various concepts related to polymorphism in C++ including: 1. Polymorphism allows accessing objects of different types through the same interface. 2. Binding refers to matching function calls to definitions, which can occur at compile-time (static binding) or runtime (dynamic binding). 3. Static binding uses function and operator overloading while dynamic binding uses virtual functions and occurs at runtime based on the object's type.

Uploaded by

madhurpatil5687
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
20 views18 pages

Unit III

The document discusses various concepts related to polymorphism in C++ including: 1. Polymorphism allows accessing objects of different types through the same interface. 2. Binding refers to matching function calls to definitions, which can occur at compile-time (static binding) or runtime (dynamic binding). 3. Static binding uses function and operator overloading while dynamic binding uses virtual functions and occurs at runtime based on the object's type.

Uploaded by

madhurpatil5687
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 18

Unit III

Polymorphism
Polymorphism means "many forms", it describes the concept that you can access objects of
different types through the same interface.

Static vs Dynamic Binding


Binding means that matching the call with the right function definition by the compiler. It
takes place either at compile time or at runtime.

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>

using namespace std;


class func { int main() {
func obj;
public: int a, b, c;
void stat(int a, int b) { a = 1;
cout << "The number of b = 1;
parameters in the first class are " << a + b; c = 1;
} obj.stat(a, b, c); //Function Call 1
cout << ".";
void stat(int a, int b, int c) { obj.stat(a, b); //Function Call 2
cout << "The number of parameters cout << ".";
in the second class are " << a + b + c; return 0;
} }
};

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.

Late Binding/ Dynamic Binding

● 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

C++ virtual function


● A C++ virtual function is a member function in the base class that you redefine in a
derived class. It is declared using the virtual keyword.

● 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.

Rules of Virtual Function


● Virtual functions must be members of some class.
● Virtual functions cannot be static members.
● They are accessed through object pointers.
● They can be a friend of another class.
● A virtual function must be defined in the base class, even though it is not used.
● The prototypes of a virtual function of the base class and all the derived classes must
be identical. If the two functions with the same name but different prototypes
● We cannot have a virtual constructor, but we can have a virtual destructor

#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()

Pure Virtual Function

● 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.

Pure virtual function can be defined as:


virtual void display() = 0;

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.

What is the Difference Between Static Binding & Dynamic Binding?

Basis of Comparison Static Binding Dynamic Binding


During compile time only. It is by
Event Occurrence During the run time.
default in C++.
Early Binding, Compile-time
Alternate Name(s) Late Binding, Runtime Binding
Binding
All the information to resolve
The compiler cannot determine all the
Information Available function calls is available
information at compile time.
simultaneously.
It is efficient while executing as all It is flexible as different types of
Advantage the information is available before objects are handled at runtime by a
runtime. single member function.
Speed It is faster. It is slower.
Function overloading or operator
Example Virtual functions in C++.
overloading in C++.

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>

using namespace std;

// Base class for reservations

class Reservation {

public:
Reservation(const string& name) : passengerName(name) {}

virtual void displayDetails() const {

cout << "Passenger: " << passengerName << endl;

virtual double calculateFare() const = 0; // Pure virtual function

protected:

string passengerName;

};

// Derived class for regular reservations

class RegularReservation : public Reservation {

public:

RegularReservation(const string& name, int numTickets)

: Reservation(name), numberOfTickets(numTickets) {}

void displayDetails() const override {

Reservation::displayDetails();

cout << "Type: Regular" << endl;

cout << "Number of Tickets: " << numberOfTickets << endl;

double calculateFare() const override {

return numberOfTickets * 100.0; // Simplified fare calculation

private:

int numberOfTickets;

};
// Derived class for special reservations (e.g., senior citizens)

class SpecialReservation : public Reservation {

public:

SpecialReservation(const string& name, int numTickets)

: Reservation(name), numberOfTickets(numTickets) {}

void displayDetails() const override {

Reservation::displayDetails();

cout << "Type: Special" << endl;

cout << "Number of Tickets: " << numberOfTickets << endl;

double calculateFare() const override {

return numberOfTickets * 75.0; // Simplified fare calculation for special reservations

private:

int numberOfTickets;

};

int main() {

vector<Reservation*> reservations;

Reservation* regReservation = new RegularReservation("Alice", 3);

Reservation* specialReservation = new SpecialReservation("Bob", 2);

reservations.push_back(regReservation);
reservations.push_back(specialReservation);

cout << "Reservation Details:" << endl;

for (const auto reservation : reservations) {

reservation->displayDetails();

cout << "Fare: $" << reservation->calculateFare() << endl;

cout << "------------------------" << endl;

// Clean up memory

for (const auto reservation : reservations) {

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>

using namespace std;

class Base {

public:
void show() {

cout << "Base class" << endl;

};

class Derived : public Base {

public:

void show() {

cout << "Derived class" << endl;

};

int main() {

Derived derivedObj;

Base* basePtr = &derivedObj; // Upcasting

basePtr->show(); // Calls the show() method of the Base class

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>

using namespace std;


class Base {

public:

virtual void show() {

cout << "Base class" << endl;

};

class Derived : public Base {

public:

void show() override {

cout << "Derived class" << endl;

};

int main() {

Base* basePtr = new Derived;

Derived* derivedPtr = dynamic_cast<Derived*>(basePtr); // Downcasting with dynamic_cast

if (derivedPtr) {

derivedPtr->show(); // Calls the show() method of the Derived class

} else {

cout << "Failed to downcast." << endl;

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>

using namespace std;

class MyClass {

public:

int value;

MyClass(int v) : value(v) {}

void display() {

cout << "Value: " << value << endl;

};

int main() {

// Creating objects using pointers

MyClass obj1(10);

MyClass* objPtr = &obj1;

// Accessing object's member using pointer


cout << "Accessing object's member using pointer: " << objPtr->value << endl;

// Calling object's method using pointer

objPtr->display();

// Creating objects on the heap using new

MyClass* heapObjPtr = new MyClass(20);

// Accessing and modifying object's member on the heap

heapObjPtr->value = 30;

cout << "Accessing object's member on the heap: " << heapObjPtr->value << endl;

// Deleting objects created on the heap

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.

Here's how the this pointer works in C++:

#include <iostream>
using namespace std;

class MyClass {

public:

int value;

MyClass(int v) : value(v) {}

void display() {

cout << "Object's value: " << this->value << endl;

// A function that modifies the object's value

void setValue(int newValue) {

this->value = newValue;

};

int main() {

MyClass obj(42);

obj.display(); // Calls the display() method with obj as 'this'

obj.setValue(88); // Calls the setValue() method with obj as 'this'

obj.display(); // Display updated value

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.

There are different types of data conversion:

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.

int numInt = 42;

double numDouble = numInt; // Implicit conversion from int to double


Explicit Conversion (Type Casting): Explicit conversion is performed when the programmer explicitly specifies the type conversion.
This is often necessary when there's a potential loss of data or when converting between incompatible types.

double numDouble = 3.14;

int numInt = static_cast<int>(numDouble); // Explicit conversion from double to int


String Conversion: Converting data to and from strings is common, especially when dealing with user input or external data
sources.

int num = 42;

string str = to_string(num); // Convert int to string

int convertedNum = stoi(str); // Convert string to int

An abstract class in object-oriented programming (OOP) is a class that cannot be


instantiated on its own but serves as a blueprint for other classes. It's meant to be
inherited by other classes, which then provide concrete implementations for its
abstract methods. Abstract classes are used to define common behavior and
properties that multiple derived classes should share.

Key characteristics of an abstract class:


1. Cannot Be Instantiated: You cannot create objects of an abstract class directly. It's
meant to be a base for other classes.
2. May Contain Abstract Methods: Abstract methods are methods declared in the
abstract class without providing an implementation. Subclasses must override these
methods with their own implementations.
3. Can Contain Concrete Methods: An abstract class can also have concrete
(implemented) methods, which provide common behavior for its subclasses.
4. Inheritance: Subclasses (also known as concrete classes) inherit from the abstract class
and provide implementations for its abstract methods.
5. Polymorphism: Abstract classes enable polymorphism. You can use a pointer or
reference of the abstract class type to refer to objects of its subclasses.

Here's an example of an abstract class in C++:

#include <iostream>

using namespace std;

// Abstract class

class Shape {

public:

virtual void displayArea() = 0; // Pure virtual method (abstract method)

};

// Concrete subclass Circle

class Circle : public Shape {

public:

Circle(double r) : radius(r) {}

void displayArea() override {

cout << "Circle Area: " << 3.14159 * radius * radius << endl;

private:

double radius;

};
// Concrete subclass Rectangle

class Rectangle : public Shape {

public:

Rectangle(double l, double w) : length(l), width(w) {}

void displayArea() override {

cout << "Rectangle Area: " << length * width << endl;

private:

double length;

double width;

};

int main() {

Circle circle(5);

Rectangle rectangle(4, 6);

// Using polymorphism

Shape* shapePtr;

shapePtr = &circle;

shapePtr->displayArea(); // Calls Circle's displayArea()

shapePtr = &rectangle;

shapePtr->displayArea(); // Calls Rectangle's displayArea()

return 0;

}
In this example:

● Shape is an abstract class with a pure virtual method displayArea().


● Circle and Rectangle are subclasses of Shape that provide concrete implementations for displayArea().
● In the main() function, we demonstrate polymorphism by using a pointer of type Shape to refer to objects of both Circle and
Rectangle classes.

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.

You might also like