Thanks to visit codestin.com
Credit goes to www.scribd.com

0% found this document useful (0 votes)
5 views14 pages

Oops Unit 4 Part 3

This document discusses polymorphism in C++, highlighting its ability to allow functions, objects, or operators to behave differently in various contexts. It details compile-time and run-time polymorphism, including function and operator overloading, and the use of virtual functions for dynamic binding. Additionally, it explains the importance of friend functions, the assignment operator, and pure virtual functions in achieving abstraction and interface definition.

Uploaded by

aviy82405
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)
5 views14 pages

Oops Unit 4 Part 3

This document discusses polymorphism in C++, highlighting its ability to allow functions, objects, or operators to behave differently in various contexts. It details compile-time and run-time polymorphism, including function and operator overloading, and the use of virtual functions for dynamic binding. Additionally, it explains the importance of friend functions, the assignment operator, and pure virtual functions in achieving abstraction and interface definition.

Uploaded by

aviy82405
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/ 14

OOPs – UNIT 4 : Part 3

Polymorphism:
Polymorphism in C++ is the ability of a function, object, or operator
to behave differently in different contexts.
• Polymorphism means "many forms" — one function or object behaves
differently based on context.
• In C++, it allows to use the same function name or operator in
different ways.
• It helps in code flexibility and reusability.
• Polymorphism makes it easier to extend and maintain programs.
• There are two types of polymorphism:
• Compile-time polymorphism (also called static polymorphism) —
achieved through function overloading and operator overloading.
• Run-time polymorphism (also called dynamic polymorphism) — achieved
using inheritance and virtual functions.

POLYMORPHISM in C++
|
|
------------------------------------------
| |
Compile-Time Polymorphism Run-Time Polymorphism
(Static/Early Binding) (Dynamic/Late Binding)
| |
--------------------- ---------------------
| | | |
Function Operator Virtual Functions Function
Overloading Overloading Overriding

Function Overloading :
read from Unit 2 Notes :
https://www.gecsheikhpura.org.in/wp-
content/uploads/sites/18/2025/05/file_68334b0298239.pdf

Operator Overloading :
Operator overloading allows to redefine how operators work for user-
defined types (like objects of a class).
• It allows operators to work with objects, not just built-in data types.
• It is done using special functions called operator functions.
• Makes the code easier to read and more intuitive.
• Only existing operators can be overloaded; new operators cannot be
created.
• Some operators like ::, :?, sizeof, and typeid cannot be overloaded.
Syntax:
return_type operator symbol (parameters) {
// your custom code here
}

P a g e 1 | 14 OOPs. GEC_SHK
Example:
#include <iostream>
using namespace std;

class Complex {
float real, imag;

public:
Complex(float r = 0, float i = 0) {
real = r;
imag = i;
}

// Overload '+' operator


Complex operator + (const Complex& obj) {
return Complex(real + obj.real, imag + obj.imag);
}

void display() {
cout << real << " + " << imag << "i" << endl;
}
};

int main() {
Complex c1(2.5, 3.5), c2(1.2, 4.8);
Complex c3 = c1 + c2; // Using overloaded +
c3.display(); // Output: 3.7 + 8.3i
return 0;
}

Operator can be overloaded using friend function :

When to Use Friend Functions

1. When the left operand is not of your class type


Case: Stream Insertion (<<) and Extraction (>>) Operators
class Student {
string name;
public:
Student(string n) : name(n) {} // another way to initialize

// MUST be a friend function (cannot be a member)


friend ostream& operator<<(ostream& os, const Student& s);
};

// Definition
ostream& operator<<(ostream& os, const Student& s) {
return os << s.name; // Accesses private 'name'
}

P a g e 2 | 14 OOPs. GEC_SHK
2. When We Need Symmetric Operator Overloading
Case: Mixed-Type Arithmetic (e.g., 5 + obj vs. obj + 5)
class Complex {
double real, imag;
public:
Complex(double r, double i) : real(r), imag(i) {}

// Friend allows both (obj + 5) and (5 + obj)


friend Complex operator+(const Complex& a, const Complex& b);
};
// Definition
Complex operator+(const Complex& a, const Complex& b) {
return Complex(a.real + b.real, a.imag + b.imag);
}

int main() {
Complex c(1, 2);
Complex c1 = c + 5; // Works (implicit conversion)
Complex c2 = 5 + c; // Also works (only possible with friend)
}

3. When We Need Access to Private Members of Multiple Classes


Case: Comparing Objects from Different Classes
class Engine; // Forward declaration
class Car {
int horsepower;
friend bool operator==(const Car&, const Engine&);
};
class Engine {
int power;
friend bool operator==(const Car&, const Engine&);
};
// Definition
bool operator==(const Car& car, const Engine& engine) {
// Accesses private members of both classes
return car.horsepower == engine.power;
}

Why Can’t These Cases Use Member Functions?


Problem with Member Why Friend is
Scenario
Function Required

Left operand is not Member functions require


Friend allows any
your class (cout << the left operand to
left operand.
obj) be this.

Member functions force the


Symmetric operators Friend allows
left operand to be the
(a + b vs. b + a) flexibility.
class object.

Access to private A member function can only


Friend can access all
members of multiple access its own class’s
declared friends.
classes privates.

P a g e 3 | 14 OOPs. GEC_SHK
Why Friend Functions Cannot Overload the Assignment Operator (=)

The assignment operator (=) must be overloaded as a member function in C++ and
cannot be a friend function. Here's why:
Key Reasons
1. Language Specification Requirements
• The C++ standard explicitly requires the assignment operator to be a non-
static member function
• This is a syntactic constraint built into the language

2. Natural Semantics of Assignment


• Assignment fundamentally modifies the left-hand operand (this object):
• obj1 = obj2; // Modifies obj1, not obj2
• Member functions have direct access to this
• Friend functions don't have a this pointer

3. Automatic Generation by Compiler


• The compiler always generates a default assignment operator if we don't
declare one
• This automatic generation only happens for member functions
• If assignment were allowed as a friend, it would create ambiguity with
compiler-generated versions

4. Chaining Behaviour
• Assignment supports chaining:
• a = b = c; // Equivalent to a = (b = c)
• This requires the operator to return *this by reference
• Only member functions can naturally return *this

P a g e 4 | 14 OOPs. GEC_SHK
Virtual Function:

• A virtual function is a member function in a base class


• declared with the virtual keyword
• can be overridden by derived classes.
• It enables runtime polymorphism, meaning the correct function is called
based on the actual object type.

Why Do We Need Virtual Functions?


1. To Achieve Runtime Polymorphism
• Without virtual, function calls are resolved at compile-time (static
binding).
• With virtual, they’re resolved at runtime (dynamic binding).
Example:
class Animal {
public:
virtual void speak() { cout << "Animal sound" << endl; } // Virtual
};

class Dog : public Animal {


public:
void speak() override { cout << "Woof!" << endl; } // Overrides
Animal::speak()
};

int main() {
Animal* animal = new Dog();
animal->speak(); // Calls Dog::speak() (not Animal::speak())
delete animal;
return 0;
}
Output:
Woof! (Because speak() is virtual.)
Without virtual, this would output "Animal sound" (which is incorrect behaviour).

2. To Support Interface Abstraction


• Virtual functions allow base classes to define interfaces while letting
derived classes provide implementations.
• Pure virtual functions (= 0) enforce that derived classes must override
them.
• Example of Abstract Class:
class Shape {
public:
virtual void draw() = 0; // Pure virtual (must be overridden)
};

class Circle : public Shape {


public:
void draw() override { cout << "Drawing a circle" << endl; }
};

int main() {
Shape* shape = new Circle();
shape->draw(); // Calls Circle::draw()
delete shape;
return 0;
}

How Virtual Functions Work (Under the Hood)


P a g e 5 | 14 OOPs. GEC_SHK
1. Virtual Function Table (vtable)
o Each class with virtual functions has a hidden table of function
pointers.
o Objects store a pointer to their class’s vtable.
2. Dynamic Dispatch
o When calling animal->speak(), the program:
1. Looks up the vtable for the actual object type (Dog, Cat,
etc.).
2. Calls the correct function from the vtable.

Key Takeaways :

Feature Non-Virtual Function Virtual Function

Binding Compile-time (static) Runtime (dynamic)

Overriding Hides base function Properly overrides

Performance Faster (no overhead) Slightly slower (vtable lookup)

Use Case Static behavior Polymorphism, interfaces

P a g e 6 | 14 OOPs. GEC_SHK
Function Overriding:
• Function overriding is a feature of object-oriented programming
• allows a derived class to provide a specific implementation of a function
that is already defined in its base class.

Key Characteristics:
1. Inheritance Requirement: Overriding requires an inheritance relationship
between classes (base and derived classes)
2. Same Signature: The function in the derived class must have exactly the
same name, return type, and parameters as in the base class
3. Runtime Polymorphism: The correct function is selected at runtime based
on the actual object type (dynamic binding)

Example:
#include <iostream>
using namespace std;

class Base {
public:
virtual void show() { // Virtual function
cout << "Base class show()" << endl;
}
};

class Derived : public Base {


public:
void show() override { // Overrides Base's show()
cout << "Derived class show()" << endl;
}
};

int main() {
Base* b; // Base class pointer
Derived d; // Derived class object

b = &d;
b->show(); // Calls Derived's show() due to overriding

return 0;
}

🤔 Why writing Base* b and calling (using base class) why not Derived d and
directly make a call (using d.show()):

The difference between using Base* b = new Derived() and directly


creating Derived d is about polymorphism and flexibility in design.

1. Direct Object Creation (No Polymorphism)


Derived d;
d.show(); // Always calls Derived::show()
• Behavior: Always calls Derived's version (no ambiguity).
• Limitation:
o You can only use Derived's interface.
o If you later want to switch to a different derived class
(e.g., Derived2), you must rewrite the code.
2. Base Pointer to Derived Object (Polymorphism)
Base* b = new Derived(); // Base pointer, Derived object
b->show(); // Calls Derived::show() if show() is virtual

P a g e 7 | 14 OOPs. GEC_SHK
Why This is Powerful:
• Runtime Polymorphism:
o The correct function (Base::show() or Derived::show()) is
decided at runtime based on the actual object type.
o Enables dynamic behavior (e.g., plugins, event handlers).
• Flexibility:
o You can switch implementations without changing client code:
Base* b;
if (condition)
b = new Derived1();
else
b = new Derived2();
b->show(); // Calls the correct version
3. Interface Programming:
• You program to the base class interface, not the implementation
• Example:
A Shape base class with draw(), where Circle and Square override it.

Approach Binding Flexibility Use Case

When you know the


Derived d; Static (compile-
Low exact type at
d.show(); time)
compile time.

When you need


Base* b = new
polymorphism or
Derived(); b- Dynamic (runtime) High
runtime
>show();
decisions.

Imagine a game with different enemies:


class Enemy { public: virtual void attack() = 0; };
class Dragon : public Enemy { void attack() override { ... } };
class Orc : public Enemy { void attack() override { ... } };

// Game logic doesn't care about the exact enemy type:


Enemy* enemy = getRandomEnemy(); // Could return Dragon or Orc
enemy->attack(); // Calls the correct attack() dynamically

Here, polymorphism is essential—the game engine doesn’t need to know the


concrete enemy type.

Example:

#include <iostream>
using namespace std;

class Shape {
public:
virtual void draw() { cout << "Generic Shape" << endl; }
};

class Circle : public Shape {


public:

P a g e 8 | 14 OOPs. GEC_SHK
void draw() override { cout << "Circle ○" << endl; }
};

class Square : public Shape {


public:
void draw() override { cout << "Square □" << endl; }
};

int main() {
Circle circle;
Square square;

Shape* shape = &circle; // Pointer to Circle


shape->draw(); // Output: Circle ○

shape = &square; // Now points to Square


shape->draw(); // Output: Square □

return 0;
}

P a g e 9 | 14 OOPs. GEC_SHK
What is Virtual in C++ ?
The virtual keyword in C++ enables dynamic polymorphism by allowing a function
to be overridden in derived classes. It -

1. Enables Runtime Binding


o Without virtual: Function calls are resolved at compile-time based on
the pointer/reference type.
o With virtual: Function calls are resolved at runtime based on
the actual object type.
2. Allows Function Overriding
o A derived class can provide its own implementation of the function.
o The override specifier (C++11) ensures correctness but is optional.
3. Creates a "Virtual Function Table" (vtable)
o The compiler generates a hidden table of function pointers for each
class with virtual methods.
o Objects store a pointer to their class’s vtable, enabling runtime
dispatch.

Example (without virtual):


class Shape {
public:
void draw() { cout << "Generic Shape" << endl; } // Not virtual
};

class Circle : public Shape {


public:
void draw() { cout << "Circle ○" << endl; } // Hides Shape::draw()
};

int main() {
Shape* shape = new Circle();
shape->draw(); // Calls Shape::draw() (compile-time binding)
delete shape;
return 0;
}
Output:
Generic Shape

Example (with virtual):


class Shape {
public:
virtual void draw() { cout << "Generic Shape" << endl; } // Now virtual
};

class Circle : public Shape {


public:
void draw() override { cout << "Circle ○" << endl; } // Proper override
};

int main() {
Shape* shape = new Circle();
shape->draw(); // Calls Circle::draw() (runtime binding)
delete shape;
return 0;
}
Output:
Circle ○

P a g e 10 | 14 OOPs. GEC_SHK
Pure Virtual Function:

a Pure Virtual Function is a virtual function

declared in a base class but is meant to be overridden in derived
classes.
• It is used to create an abstract class.
• Syntax
virtual returnType functionName() = 0;
The = 0 part makes the function pure virtual.
Purpose :
1. Force Derived Classes to provide their own implementation.
2. Define interfaces (contracts) that derived classes must follow.
3. Achieve abstraction by hiding implementation details.

Example:
class Shape {
public:
virtual void draw() = 0; // Pure virtual function
};
class Circle : public Shape {
public:
void draw() override { // Must implement pure virtual function
cout << "Drawing a circle";
}
};

int main() {
Shape* s = new Circle();
s->draw(); // Calls Circle::draw()
delete s;
return 0;
}

Key Differences
Feature Virtual Function Pure Virtual Function

Syntax virtual void func(); virtual void func() = 0;

Yes (base class provides No (derived classes must


Has Implementation?
default) implement)

Base Class
Yes No (abstract class)
Instantiable?

Mandatory overriding
Purpose Optional overriding
(interface)

P a g e 11 | 14 OOPs. GEC_SHK
Abstract Class :

• an abstract class is a class that cannot be instantiated


• designed to be a base class for other classes.
• It contains at least one pure virtual function.
• Example:
class AbstractClass {
public:
virtual void display() = 0; // Pure virtual function
};

#include <iostream>
using namespace std;

class Animal {
public:
virtual void sound() = 0; // Pure virtual function
};

class Dog : public Animal {


public:
void sound() override {
cout << "Bark" << endl;
}
};

int main() {
// Animal a; // Error: Cannot instantiate abstract class
Dog d; // Dog is concrete because it overrides all pure virtual functions
d.sound(); // Output: Bark
return 0;
}

Abstract Class:
• Cannot be directly instantiated :
o we cannot create objects of it.
class Shape {
public:
virtual void draw() = 0; // Pure virtual
};

int main() {
Shape s; // Error: Cannot instantiate abstract class
return 0;
}
• Derived classes must override all pure virtual functions to become
concrete.
o If a derived class does not override all pure virtual functions
of its base class, then that derived class also becomes an
abstract class:
#include <iostream>
using namespace std;

class Shape {
public:
virtual void draw() = 0; // Pure virtual function
};

P a g e 12 | 14 OOPs. GEC_SHK
class Circle : public Shape {
// No override of draw()
};

int main() {
// Circle c;
// Error: Circle is still abstract because it didn't override draw()
return 0;
}
To make it concrete derived class must override all pure virtual
o
functions:
class Circle : public Shape {
public:
void draw() override {
cout << "Drawing Circle" << endl;
}
};
• Must have at least one is required (virtual func() = 0;)
• Abstract classes can also contain normal (non-pure) methods

Interface :
An interface in C++ is typically an abstract class where:
• All member functions are pure virtual (= 0)
• No data members (ideally)
• Meant to define a contract that derived classes must follow
• Example
#include <iostream>
using namespace std;

class IPrintable { // 'I' prefix is a common naming convention for interfaces


public:
virtual void print() = 0; // Pure virtual function
virtual ~IPrintable() {} // Always add a virtual destructor in interfaces
};

class Document : public IPrintable {


public:
void print() override {
cout << "Printing document..." << endl;
}
};

• Characteristics:
Property Description
All methods are pure
Each function has = 0
virtual
Interfaces should not define function bodies (except
No implementation allowed
destructor)
No data members Keep the interface clean and behavior-focused
Cannot be instantiated Interfaces are abstract classes
Supports multiple
A class can implement multiple interfaces
inheritance

P a g e 13 | 14 OOPs. GEC_SHK
Comparison Table

Class with at least Abstract class with Class with all


Definition one pure virtual only pure virtual functions
function functions implemented
Can be
instantiated? ❌ No ❌ No ✅ Yes

Can have data ⚠️ Not recommended


members? ✅ Yes ✅ Yes
(ideally no data)
Pure virtual
functions? ✅ At least one ✅ All functions ❌ None

Normal member ❌ No (only pure


functions? ✅ Yes (optional) ✅ Yes
virtual functions)
Base for Define strict
Full implementation,
Use case polymorphism or contracts (like
used directly
partial abstraction APIs)
Multiple
inheritance ✅ Yes ✅ Yes ✅ Yes
support
Virtual ✅ Required (for ✅ Optional (but
destructor? ✅ Recommended
proper cleanup) good practice)

Example:

// Interface
class IShape {
public:
virtual void draw() = 0;
virtual ~IShape() {} // always add virtual destructor
};

// Abstract Class
class Shape {
public:
virtual void draw() = 0;
void move() { cout << "Moving shape" << endl; }
};

// Concrete Class
class Circle : public IShape {
public:
void draw() override {
cout << "Drawing Circle" << endl;
}
};

P a g e 14 | 14 OOPs. GEC_SHK

You might also like