Operator Overloading & Inheritance
Module – 4
Virtual Functions and Polymorphism: Virtual Functions, The Virtual Attribute is
Inherited, Virtual Functions are Hierarchical, Pure Virtual Functions, Using Virtual
Functions, Early vs Late Binding.
Templates: Generic Functions, Applying Generic Functions, Generic Classes, The
typename and export Keywords, The Power of Templates.
Polymorphism:
Polymorphism means one function name can work in different ways depending on
the situation.
C++ supports two types of polymorphism:
1. Compile-time polymorphism (Early Binding)
Happens during compilation.
i. Function overloading
ii. Operator overloading
Example:
add(int, int) and
add(double, double) — same name, different behavior.
2. Run-time polymorphism (Late Binding)
Happens during program execution.
i. Virtual functions
Virtual functions:
A virtual function is a function written in a base class, and it is meant to be redefined
(overridden) in a derived class.
Because the base class provides a common interface and the derived classes provide
specific versions of that function.
Syntax:
class Base {
public:
. 1
Operator Overloading & Inheritance
virtual void display()
{
// function body
}
};
Example:
class Staff
{
public:
virtual void role() {
cout << "General staff member\n";
}
};
class Teacher : public Staff
{
public:
void role() override
{
cout << "Teacher: Teaches subjects\n";
}
};
class Admin : public Staff
{
public:
void role() override
{
cout << "Admin: Manages office work\n";
}
};
. 2
Operator Overloading & Inheritance
int main()
{
Staff *ptr;
Teacher t;
Admin a;
ptr = &t;
ptr->role(); // Teacher version
ptr = &a;
ptr->role(); // Admin version
return 0;
}
Here, override keyword tells compiler this function is intended to override a virtual
function in the base class.
Calling a Virtual Function Through a Base Class Reference:
A base-class reference can also call a virtual function, just like a base-class pointer.
o Base-class pointer base *ptr
o Base-class reference base &ref
Because a reference behaves like an implicit pointer.
When you pass an object using a reference of base class, the virtual function that gets
executed depends on the actual object (base or derived), not the reference type.
Syntax:
void f(base &r)
{
[Link](); // calls the correct version dynamically
}
Why is this useful?
Because in many programs, we pass objects to functions.
If the function receives parameters by reference, polymorphism will still work.
Whether you use a base pointer or base reference, virtual functions always call the
derived version based on the actual object.
. 3
Operator Overloading & Inheritance
Example:
class Staff {
public:
virtual void work() {
cout << "General staff work.\n";
}
};
class Teacher : public Staff {
public:
void work() override {
cout << "Teacher: Conducts classes.\n";
}
};
class Admin : public Staff {
public:
void work() override {
cout << "Admin: Handles office tasks.\n";
}
};
// Function taking base-class reference
void showWork(Staff &s) {
[Link](); // virtual function call
}
int main() {
Staff st;
Teacher t;
Admin a;
. 4
Operator Overloading & Inheritance
showWork(st); // calls Staff::work()
showWork(t); // calls Teacher::work()
showWork(a); // calls Admin::work()
return 0;
}
The Virtual Attribute Is Inherited:
If a function is virtual in the base class, it stays virtual in all derived classes.
You don’t need to write virtual again in the derived class, it is automatically virtual.
If a derived class becomes a base class for another class, the function is still virtual.
Every level in the inheritance chain can override that virtual function.
No matter how many classes inherit it, the function always remains virtual.
This allows correct function overriding and runtime polymorphism even in
multilevel inheritance.
Example:
class base {
public:
virtual void vfunc()
{
cout << "This is base's vfunc().\n";
}
};
class derived1 : public base {
public:
void vfunc()
{
cout << "This is derived1's vfunc().\n";
}
};
. 5
Operator Overloading & Inheritance
/* derived2 inherits virtual function vfunc()
from derived1. */
class derived2 : public derived1
{
public:
// vfunc() is still virtual
void vfunc() {
cout << "This is derived2's vfunc().\n";
}
};
int main()
{
base *p, b;
derived1 d1;
derived2 d2;
// point to base
p = &b;
p->vfunc(); // access base's vfunc()
// point to derived1
p = &d1;
p->vfunc(); // access derived1's vfunc()
// point to derived2
p = &d2;
p->vfunc(); // access derived2's vfunc()
return 0;
}
As expected, the preceding program displays this
output:
This is base's vfunc().
This is derived1's vfunc().
This is derived2's vfunc().
. 6
Operator Overloading & Inheritance
In this case, derived2 inherits derived1 rather than
base, but vfunc() is still virtual.
Virtual Functions Are Hierarchical:
If a derived class does not provide its own version of a virtual function, it simply
inherits and uses the base class version.
A virtual function starts at the base class. The base class marks a function as virtual.
Derived classes may override it, but they don’t have to. Overriding is optional, not
compulsory.
If a derived class does NOT override the virtual function. Then the derived class
automatically uses the base class version.
Example:
class base {
public:
virtual void vfunc()
{
cout << "This is base's vfunc().\n";
}
};
class derived1 : public base {
public:
void vfunc()
{
cout << "This is derived1's vfunc().\n";
}
};
class derived2 : public base
{
public:
// vfunc() not overridden by derived2, base's is used
};
. 7
Operator Overloading & Inheritance
int main()
{
base *p, b;
derived1 d1;
derived2 d2;
// point to base
p = &b;
p->vfunc(); // access base's vfunc()
// point to derived1
p = &d1;
p->vfunc(); // access derived1's vfunc()
// point to derived2
p = &d2;
p->vfunc(); // use base's vfunc()
return 0;
}
Pure Virtual Functions:
The base class cannot give a meaningful definition for a function, or You want to
force every derived class to override a function.
A virtual function with NO body in the base class.
A class with a pure virtual function becomes an “abstract class.” You cannot create
objects of that class.
Purpose
o Forces common interface for all child classes.
o Base class gives structure, derived classes give meaning.
A pure virtual function is a virtual function that has no definition within the base
class.
Syntax:
virtual type func-name(parameter-list) = 0;
. 8
Operator Overloading & Inheritance
Real-time Example: College – Employee System:
Because “Employee” is too general.
A teaching staff may have salary based on:
o Basic pay
o Allowances
o Workload hours
A non-teaching staff may have salary based on:
o Fixed monthly pay
o Overtime hours
class Employee
{
public:
virtual void calculateSalary() = 0; // pure virtual
};
So, the base class does not know how to calculate salary for each type.
Thus, no meaningful definition is possible make it a pure virtual function.
Example:
class Employee {
public:
virtual void work() = 0; // Pure virtual function
};
// Derived class: Teaching Staff
class TeachingStaff : public Employee
{
public:
void work() override
{
cout << "Teaching staff is teaching students." << endl;
}
};
. 9
Operator Overloading & Inheritance
// Derived class: Non-Teaching Staff
class NonTeachingStaff : public Employee {
public:
void work() override
{
cout << "Non-teaching staff is doing administrative work." << endl;
}
};
int main()
{
TeachingStaff t1;
NonTeachingStaff n1;
Employee* e1 = &t1;
Employee* e2 = &n1;
e1->work(); // Calls TeachingStaff version
e2->work(); // Calls NonTeachingStaff version
return 0;
}
Abstract Classes
A class that contains at least one pure virtual function is said to be abstract.
Because an abstract class contains one or more functions for which there
is no definition (that is, a pure virtual function), no objects of an abstract
class may be created.
Instead, an abstract class constitutes an incomplete type that is used as a
foundation for derived classes.
. 10
Operator Overloading & Inheritance
Virtual Functions and Abstract Classes:
Interface
Defines what a class can do.
Includes function names, parameters, and structure.
Example: A base class Convert defines compute(), getInit(), getConv().
Multiple Methods
Each derived class implements the interface differently.
The same function name can have different behavior in different derived classes.
Example:
LitersToGallons::compute() converts liters to gallons
KmToMiles::compute() converts kilometers to miles
// Base class (abstract)
class Convert
{
protected:
double val1; // initial value
double val2; // converted value
public:
void setInit(double v) { val1 = v; }
double getInit() { return val1; }
double getConv() { return val2; }
virtual void compute() = 0; // pure virtual function
};
// Derived class: Liters to Gallons
class LitersToGallons : public Convert
{
public:
void compute() override
{
val2 = val1 * 0.264172; // 1 liter = 0.264172 gallons
. 11
Operator Overloading & Inheritance
};
// Derived class: Kilometers to Miles
class KmToMiles : public Convert
{
public:
void compute() override
{
val2 = val1 * 0.621371; // 1 km = 0.621371 miles
}
};
int main() {
LitersToGallons lg;
KmToMiles km;
// Liters to gallons
[Link](10);
[Link]();
cout << [Link]() << " liters = "
<< [Link]() << " gallons" << endl;
// Kilometers to miles
[Link](5);
[Link]();
cout << [Link]() << " km = "
<< [Link]() << " miles" << endl;
return 0;
}
. 12
Operator Overloading & Inheritance
Early Binding vs Late Binding:
Early Binding
Feature Late Binding (Run-Time)
(Compile-Time)
Function call is resolved at
Definition Function call is resolved at run time.
compile time.
Compiler knows exactly Compiler does not know the exact function;
How it works which function to call while decision is made based on the object type at
compiling. runtime.
- Normal function calls-
Example in - Virtual functions- Function call via base class
Function overloading-
C++ pointer/reference
Operator overloading
ATM withdrawal system: Institute staff attendance system: Base pointer
Real-time Withdraw function always Employee* e points to TeachingStaff or
Example deducts amount using fixed NonTeachingStaff. Calling e->work()
logic known at compile time. executes the correct version at run time.
Fast execution because Flexible and adaptable; allows runtime
Advantages function is known at compile decisions without writing multiple conditional
time. statements.
Less flexible; cannot change Slightly slower because function resolution
Disadvantages
behavior at runtime. happens at runtime.
. 13
Operator Overloading & Inheritance
Templates:
Templates are one of C++’s most powerful and flexible features. They allow
programmers to create generic functions and classes, where the data type is treated
as a parameter.
This means that a single function or class can operate on different data types without
the need to write separate versions for each type.
Templates provide code reusability, flexibility, and type safety.
Two types of templates:
1. Function Templates
2. Class Templates
Function Templates (Generic Functions):
A generic function performs operations that can be applied to different data types.
The data type is passed as a parameter to the function (placeholder type).
Allows a single algorithm to work with integers, floats, chars, or other types without
rewriting the code.
Many algorithms, like sorting or swapping, are logically the same for all data types,
making them suitable for generic functions.
A generic function is created using the template keyword.
Syntax:
template <class T> retType funcName(parameters)
{
// function body using T
}
Example:
// Function template using 'typename' // Function template using 'class'
template <typename T> template <class T>
void swapArgs(T &a, T &b) { void swapArgs(T &a, T &b) {
T temp = a; T temp = a;
a = b; a = b;
b = temp; } b = temp; }
. 14
Operator Overloading & Inheritance
int main()
{
char a = 'x', b = 'y';
swapArgs(a, b); // swaps chars
cout << "Swapped chars: " << a << ", "
<< b << endl;
return 0;
}
A Function with Two Generic Types
Templates let you write one function that works with many types.
Using two template parameters allows the function to accept two different types at
the same time.
Syntax:
template <class T1, class T2>
return_type(T1/T2/auto) function_name(T1 a, T2 b)
{
// function body
}
Example:
template <class T1, class T2>
void show(T1 x, T2 y)
{
cout << x << " " << y << endl;
}
int main()
{
show(10, 3.14); // T1 = int, T2 = double
show('A', "Hello"); // T1 = char, T2 = const char*
return 0;
}
. 15
Operator Overloading & Inheritance
1. Write a template that adds two different types and returns the result using auto.
2. Write a template with two generic types that prints the two values.
Explicitly Overloading a Generic Function:
Explicitly overloading a generic function means writing a normal, non-template
function for a specific data type even when a template version already exists.
This overloaded function takes priority over the template function for that particular
type, and therefore it overrides (or hides) the generic template version.
Example:
// Generic template function
template <class X>
void swapargs(X &a, X &b)
{
X temp;
temp = a;
a = b;
b = temp;
cout << "Inside template swapargs.\n";
}
// This overloads the generic version of swapargs() for ints
void swapargs(int &a, int &b)
{
int temp;
temp = a;
a = b;
b = temp;
cout << "Inside swapargs int specialization.\n";
}
. 16
Operator Overloading & Inheritance
int main()
{
int i = 10, j = 20;
double x = 10.1, y = 23.3;
char a = 'x', b = 'z';
cout << "Original i, j: " << i << " " << j << endl;
cout << "Original x, y: " << x << " " << y << endl;
cout << "Original a, b: " << a << " " << b << endl;
swapargs(i, j); // calls overloaded int version
swapargs(x, y); // calls generic template
swapargs(a, b); // calls generic template
cout << "Swapped i, j: " << i << " " << j << endl;
cout << "Swapped x, y: " << x << " " << y << endl;
cout << "Swapped a, b: " << a << " " << b << endl;
return 0;
}
. 17
Operator Overloading & Inheritance
Overloading a Function Template:
A function template can be overloaded just like normal functions.
You can create multiple versions of a template with different parameter lists.
The compiler chooses the correct version based on the number and types of
arguments.
Example:
// First version of f() template.
template <class X> void f(X a)
{
cout << "Inside f(X a)\n";
}
// Second version of f() template.
template <class X, class Y> void f(X a, Y b)
{
cout << "Inside f(X a, Y b)\n";
}
int main()
{
f(10); // calls f(X)
f(10, 20); // calls f(X, Y)
return 0;
}
Generic (Template) Classes:
A generic class is a class where the data type is unspecified and is defined as a
template parameter.
o This allows the same class logic (algorithms) to work for any data type.
o The compiler generates the correct class type automatically when an object is
created.
A stack or queue can store int, char, or user-defined objects using the same class
definition.
. 18
Operator Overloading & Inheritance
Syntax:
template <class T>
class ClassName {
// Members using type T
T data;
public:
void setData(T value) { data = value; }
T getData() { return data; }
};
Creating an Object
ClassName<int> obj1; // Object storing int
ClassName<char> obj2; // Object storing char
ClassName<int, char> obj1; // Object storing int,char
Simple Example: Write a generic class Stack in C++ that can store any type of
data.
template <class T>
class Stack {
T items[100];
int top;
public:
Stack() { top = -1; }
void push(T value) {
if(top < 99)
items[++top] = value;
else
cout << "Stack overflow!\n";
}
. 19
Operator Overloading & Inheritance
T pop()
{
if(top >= 0)
return items[top--];
else
{
cout << "Stack underflow!\n"; return T();
}
}
};
int main()
{
Stack<int> intStack;
[Link](10);
[Link](20);
cout << "Popped from intStack: " << [Link]() << endl;
Stack<char> charStack;
[Link]('A');
[Link]('B');
cout << "Popped from charStack: " << [Link]() << endl;
return 0;
}
. 20
Operator Overloading & Inheritance
The Power of Templates:
Templates help achieve reusable code and create frameworks usable in a variety of
programming situations.
Generic classes and functions allow the same algorithms to work for any type of
data, saving the tedium of separate implementations.
Once written and debugged, a template class is a solid software component usable
with confidence.
Templates add abstraction but still compile to high-performance object code; the STL
is built on templates.
Although template syntax can seem intimidating at first, the rewards are well worth
the time.
. 21