UNIT II – INHERITANCE &
POLYMORPHISM
Inheritance
• The capability of a class to derive properties and characteristics from
another class is called Inheritance.
• Inheritance is one of the most important features of Object Oriented
Programming in C++.
• Re-usability is the main advantage of inheritance.
Base class
• A class in Object-Oriented Programming language, from which other
classes are derived.
• The class which inherits the base class has all members of a base class
as well as can also have some additional properties.
• The Base class members and member functions are inherited to
Object of the derived class.
• A base class is also called parent class or superclass.
Derived Class
• A class that is created from an existing class.
• The derived class inherits all members and member functions of a
base class.
• The derived class can have more functionality with respect to the
Base class and can easily access the Base class.
• A Derived class is also called a child class or subclass.
SYNTAX
class Base_Class_Name
{
// members....
// member function
}
class Derived_Class_Name : access specifier Base_Class_Name
{
// members....
// member function
}
Eg:
class derived:private base
Types Of Inheritance in C++
The inheritance can be classified on the basis of the relationship
between the derived class and the base class.
• Single inheritance
• Multilevel inheritance
• Multiple inheritance
• Hierarchical inheritance
• Hybrid inheritance
Single Inheritance
• In single inheritance, a class is allowed to inherit from only one class.
• one base class is inherited by one derived class only.
Syntax
class subclass_name : access_mode base_class
{
// body of subclass
};
Single Inheritance
Syntax: Example:
class A class Vehicle {
{ public:
... .. ... Vehicle() { cout << "This is a
}; Vehicle\n"; }
class B: public A };
{
... .. ... // sub class derived from a single
base classes
}; class Car : public Vehicle {
public:
Car() { cout << "This Vehicle is
Car\n"; }
};
Multiple Inheritance
• Multiple Inheritance is a feature of C++ where a class can inherit from
more than one class.
• One subclass is inherited from more than one base class.
Syntax
class subclass_name : access_mode base_class1, access_mode base_class2,
....
{
// body of subclass
};
Multiple Inheritance
Syntax:
class A
{
... .. ...
};
class B
{
... .. ...
};
class C: public A, public B
{
... ... ...
};
Example:
class Vehicle {
public:
Vehicle() { cout << "This is a Vehicle\n"; }
};
// second base class
class FourWheeler {
public:
FourWheeler()
{ cout << "This is a 4 Wheeler\n"; }
};
// sub class derived from two base classes
class Car : public Vehicle, public FourWheeler {
public:
Car() { cout << "This 4 Wheeler Vehicle is a Car\n"; }
};
Multilevel Inheritance
In this type of inheritance, a derived class is created from another derived class and that derived
class can be derived from a base class or any other derived class. There can be any number of
levels.
Syntax
class derived_class1: access_specifier base_class
{
... .. ...
}
class derived_class2: access_specifier derived_class1
{
... .. ...
}
.....
Multilevel Inheritance
Example:
class Vehicle {
public:
Vehicle() { cout << "This is a Vehicle\n"; }
};
// first sub_class derived from class vehicle
class fourWheeler : public Vehicle {
public:
fourWheeler() { cout << "4 Wheeler Vehicles\n"; }
};
// sub class derived from the derived base class fourWheeler
class Car : public fourWheeler
{
public:
Car() { cout << "This 4 Wheeler Vehicle is a Car\n"; }
};
Hierarchical Inheritance
• In this type of inheritance, more than one subclass is inherited from a
single base class.
• More than one derived class is created from a single base class.
Syntax
class derived_class1: access_specifier base_class
{
... .. ...
}
class derived_class2: access_specifier base_class
{
... .. ...
}
Hierarchical Inheritance
Syntax:
class G { };
class B : public G { };
class E : public G { };
class A : public B { };
class C : public B { };
class D : public E { };
class F : public E { };
Example:
class Vehicle {
public:
Vehicle() { cout << "This is a Vehicle\n"; }
};
// first sub class
class Car : public Vehicle {
public:
Car() { cout << "This Vehicle is Car\n"; }
};
// second sub class
class Bus : public Vehicle {
public:
Bus() { cout << "This Vehicle is Bus\n"; }
};
Vehicle
/ \
Land Water
/ \ / \
Car Bus Boat Ship
Creating a Car:
This is a Vehicle
This is a Land Vehicle
This Land Vehicle is a Car
Creating a Ship:
This is a Vehicle
This is a Water Vehicle
This Water Vehicle is a Ship
// Base class
class Vehicle { // Second level subclasses
class Ship : public Water {
public:
class Car : public Land {
public: public:
Vehicle() { Car() { Ship() {
cout << "This is a Vehicle\n"; cout << "This Land Vehicle is
a Car\n"; cout << "This Water Vehicle is a
}
}}; Ship\n";
};
class Bus : public Land { }
// First level subclasses
public: };
class Land : public Vehicle {
Bus() {
public: int main() {
cout << "This Land Vehicle is
Land() { cout << "Creating a Car:\n";
a Bus\n";
cout << "This is a Land Vehicle\n"; Car c;
}
}
};
}; class Boat : public Water { cout << "\nCreating a Ship:\n";
class Water : public Vehicle { public:
Ship s;
public: Boat() {
Water() { return 0;
cout << "This Water Vehicle
cout << "This is a Water Vehicle\n"; is a Boat\n"; }
}}; }};
Hybrid Inheritance
• Hybrid Inheritance is implemented by combining more than one type
of inheritance.
• For example: Combining Hierarchical inheritance and Multiple
Inheritance will create hybrid inheritance in C++
• There is no particular syntax of hybrid inheritance. We can just
combine two of the above inheritance types.
Hybrid Inheritance
This diagram represents a hybrid
inheritance model that combines single,
hierarchical, and multiple inheritance,
where Class E inherits from both Class F and
Class G, and Class A and Class C inherit from
Class B.
Syntax: class A : public B
class F {
{ ... .. ...
... .. ... }
} class C : public B
class G {
{ ... .. ...
... .. ... }
}
class B : public F
{
... .. ...
}
class E : public F, public G
{
... .. ...
}
Example:
class Vehicle { // first sub class
public: class Car : public Vehicle {
Vehicle() public:
{ Car()
cout << "This is a Vehicle\n"; } {
}; cout << "This Vehicle is a Car\n"; }
};
// base class
class Fare // second sub class
{ class Bus : public Vehicle, public Fare {
public: public:
Fare() Bus()
{ {
cout << "Fare of Vehicle\n"; } cout << "This Vehicle is a Bus with Fare\n"; }
}; };
Different types of Inheritance - Programs
1) Single Inheritance - Math
Operations int subtract(int a, int b)
#include <iostream> {
using namespace std; return a - b;
// Base class }
class MathOperations };
int main()
{
{
public:
AdvancedMath math;
int add(int a, int b) cout << "Addition: " << [Link](10, 5) <<
{ endl; // From MathOperations
return a + b; cout << "Subtraction: " <<
} [Link](10, 5) << endl; // From
}; AdvancedMath
return 0;
// Derived class
}
class AdvancedMath : public Output:
MathOperations
Addition: 15
{
Subtraction: 5
Public:
2) Multiple Inheritance - Student Details
#include <iostream> // Derived class inheriting from both Person and Marks
using namespace std; class Student : public Person, public Marks
// First base class {
class Person public:
{ void showStudent()
public: {
void showPerson() cout << "Student details are displayed above" << endl;
{ }
cout << "Name: John, Age: 20" << endl; };
} int main()
}; {
// Second base class Student obj;
class Marks [Link](); // Inherited from Person
{ [Link](); // Inherited from Marks
public: [Link](); // Student's own method
void showMarks() return 0;
{ }
cout << "Math: 90, Science: 85, English: 88" << endl; Output:
} Name: John, Age: 20
}; Math: 90, Science: 85, English: 88
Student details are displayed above
3) Multilevel Inheritance - Vehicle Example
#include <iostream> // Derived class (inherits from FourWheeler)
using namespace std; class Car : public FourWheeler
// Base class {
class Vehicle public:
{ void showCar()
public: { cout << "This is a car with four wheels." << endl; }
void showVehicle() };
{ int main()
cout << "This is a vehicle." << endl; {
} Car obj;
}; [Link](); // Inherited from Vehicle
[Link](); // Inherited from FourWheeler
// Intermediate class (inherits from Vehicle) [Link](); // Car's own method
class FourWheeler : public Vehicle return 0;
{ }
public: Output:
void showFourWheeler() This is a vehicle.
{ cout << "This is a four-wheeler vehicle." << endl; } This is a four-wheeler vehicle.
}; This is a car with four wheels.
4) Hierarchical Inheritance - Animal Example
#include <iostream> void meow()
using namespace std; { cout << "Cat meows: Meow! Meow!" << endl; }
// Base class };
class Animal int main()
{ {
public: Dog d; Cat c;
void showAnimal() cout << "Dog Class Output:" << endl;
{ [Link](); // Inherited from Animal
cout << "I am an animal." << endl; [Link](); // Dog's own method
} cout << "Cat Class Output:" << endl;
}; [Link](); // Inherited from Animal
// Derived class 1 [Link](); // Cat's own method
class Dog : public Animal return 0;
{ }
public: Output:
void bark() Dog Class Output:
{ cout << "Dog barks: Woof! Woof!" << endl; } I am an animal.
}; Dog barks: Woof! Woof!
// Derived class 2 Cat Class Output:
class Cat : public Animal I am an animal.
{ Cat meows: Meow! Meow!
public:
Protected Members
• Protected access
• Intermediate level of protection between public and private
inheritance
• Derived-class members can refer to public and protected members of
the base class simply by using the member names
• Protected data breaks encapsulation to some extent (i.e., having only
protected members in a class breaks encapsulation from the
perspectives of a parent class / base class)
Private/ public /protected Inheritance
When a class is inherited using the public keyword, the access levels of
the base class members remain the same in the derived class:
• Public members of the base class remain public in the derived class.
• Protected members of the base class remain protected in the derived
class.
• Private members of the base class are not directly accessible in the
derived class.
Public members remain public---Protected members remain
protected--Private members are not accessible.
When a class is inherited using the protected keyword:
• Public members of the base class become protected in the derived
class.
• Protected members remain protected.
• Private members remain inaccessible.
When a class is inherited using the private keyword:
• Public and protected members of the base class become private in
the derived class.
• Private members remain inaccessible.
Summary Table
Base Class Public Protected Private
Access Inheritance Inheritance Inheritance
Public
public protected private
members
Protected
protected protected private
members
Private Not Not Not
members inherited inherited inherited
In C++, public inheritance preserves access levels, protected inheritance converts public members to protected, and
private inheritance converts both public and protected members to private, while private members of the base class are
not accessible in derived classes.
#include <iostream> class Derived : public Base {
public:
using namespace std; void show() {
cout << pub << endl; // OK (public → public)
cout << prot << endl; // OK (protected → protected)
class Base { cout << priv; ERROR (never inherited)
}
public: };
int pub = 1;
protected: class Derived : protected Base {
public:
int prot = 2; void show() {
cout << pub << endl; // OK (public → protected)
cout << prot << endl; // OK (protected →
private: protected)
cout << priv; ERROR
int priv = 3; }
};
};
Name the Inheritance TYPE
Problem 1 (incorporating all types of
inheritance – hybrid inheritance)
Write a C++ program for a University Management System that
demonstrates all types of inheritance in one code: create a base class
(Person) containing common attributes (name and age) and a function to
display them; derive classes Faculty and Student from Person (hierarchical
inheritance) where Faculty includes a faculty ID and Student includes a
student ID; extend Faculty with a Department class (multilevel inheritance)
that adds a department name; implement a Researcher class that inherits
from both Faculty and Student (multiple inheritance) to represent a dual
role; and finally, create a Teaching Assistant class using virtual inheritance
from Faculty and Student (hybrid inheritance) to resolve the diamond
problem, then in main(), instantiate objects of each type, set their
attributes, and display their details.
Constructors and destructors in derived class
• Both base and derived classes can have their own constructors and
destructors.
• The constructors and destructors of the base class are automatically
called when an object of the derived class is created or destroyed.
• Constructors of the base class are called before the constructors of
the derived class and they initialize the base class members.
• Destructors of the base class are called after the destructors of the
derived class and they clean up the base class members.
• The order of the constructor and destructor calls is important to
ensure that the base class is properly initialized and cleaned up
before and after the derived class.
Example:
class Human
{
public:
Human()
{ cout<<“Human constructor”<<endl;}
~Human()
{ cout<<“Human destructor “<<endl;}
}
class nature:public Human
nature()
{ cout<<“Nature constructor”<<endl;}
~nature()
{ cout<<“Nature destructor “<<endl;}
};
int main()
{
nature bird;
cout<<“main function “ << endl;
return 0;
}
The main() function starts execution first, but constructors of objects created inside main() execute
before the statements in main(), and destructors execute when main() ends.
The order of execution is
1. base constructor
2. derived constructor
3. Main function
4. derived destructor
5. base destructor
Output
Human constructor
Nature constructor
Main function
Nature destructor
Human destructor
Overriding
• Defining a function in the derived class with same
name as in the parent class is called overriding.
• Function overriding happens when:
a derived class provides its own implementation of a function
that already exists in the base class
with the same function name and same parameters.
• In C++, the base class member can be overridden by
the derived class function with the same signature as
the base class function.
• Overriding is used to provide different
implementations of a function so that a more specific
behavior can be realized.
Function overriding is a feature of OOP in which a
derived class redefines a base class function with
the same signature.
Rules for Function Overriding
• Function name must be same
• Parameters must be same Return type must be same
• Must involve inheritance
• Base function should be virtual
• Happens at runtime
Example
#include <iostream> class B: public A int main()
{ {
using namespace std; A ob1;
class A int b;
B ob2;
public:
{ [Link](); // calls derived class
B() show() function
int a;
{ return 0;
public: b = 20; }
A() } Output: 20
{ void show() Note:
a = 10; } { The derived class functions override base class
void show() cout << b; functions. The problem with this is, the derived class
} objects can not access base class member functions
{ which are overridden in derived class.
cout << a; }; Base class pointer can point to derived class objects;
but it has access only to base members of that derived
} class.
}; Therefore, pA = &ob2; is allowed. But pa->show() will
call Base class show() function and o/p
would be 10.
Example: Void printvalue()
#include <iostream> {
using namespace std; cout<<age;
class human cout<<name;
{ cout<<height;
private : }
int age; };
public: int main()
string name; {
protected : Man boy;
int height; [Link]();
}; return 0;
Class Man:public human }
{
public:
Casting Class pointers and Member Functions
• Casting is the process of converting a variable, expression, function
argument, or return value from one data type to another
• It helps in correctly interpreting memory layouts and accessing the
right function implementations.
• C++ provides four types of casting operators:
1. static_cast, to convert one type to another type;
2. const_cast, to cast away the ``const-ness'' or ``volatile-ness'' of a
type;
3. dynamic_cast, for safe navigation of an inheritance hierarchy; and
4. reinterpret_cast, to perform type conversions on un-related types.
static_cast
• The static_cast operator is the most commonly used casting operator
in C++.
• It performs compile-time type conversion and is mainly used for
explicit conversions that are considered safe by the compiler.
• used to convert between related types, such as numeric types or
pointers in the same inheritance hierarchy.
• Syntax
static_cast <new_type> (exp);
• exp: Data to be converted.
• new_type: Desired type of expression
static_cast
Compile-time cast
Primitive type conversion
int a = 10;
double b = static_cast<double>(a); // int → double
dynamic_cast
• Mainly used to perform downcasting (converting a pointer/reference of a
base class to a derived class) in polymorphisms and inheritance.
• It ensures type safety by performing a runtime check to verify the validity
of the conversion.
• Used to safely convert a base class pointer to a derived class pointer at
runtime.
• If the conversion is not possible, dynamic_cast returns a null pointer (for
pointer conversions) or throws a bad_cast exception (for reference
conversions).
• Syntax
dynamic_cast <new_type> (exp);
include <iostream> int main()
using namespace std; {
Human* h = new Nature(); // base pointer → derived object
class Human
{ // Try to convert base pointer to derived pointer
public: Nature* n = dynamic_cast<Nature*>(h);
virtual void speak() // makes class polymorphic
{ if (n != nullptr)
cout << "Human speaks" << endl; {
} cout << "Cast successful!" << endl;
}; n->fly();
}
class Nature : public Human else
{ {
public: cout << "Cast failed!" << endl;
void fly() }
{
cout << "Bird flies" << endl; delete h;
} return 0;
}; }
OUTPUT
Cast successful!
Bird flies
const_cast
• Used to modify the const or volatile qualifier of a variable.
• It allows programmers to temporarily remove the constancy of an
object and make modifications.
• Caution must be exercised when using const_cast, as modifying a
const object can lead to undefined behavior.
• Syntax
const_cast <new_type> (exp);
reinterpret_cast
• used to convert the pointer to any other type of pointer.
• It does not perform any check whether the pointer converted is of
the same type or not.
• Syntax
reinterpret_cast <new_type> (exp);
Note: const_cast and reinterpret_cast are generally not recommended
as they are vulnerable to different kinds of errors.
Example // Reinterpreting the pointer as a char
pointer
#include <iostream> char* charptr =
reinterpret_cast<char*>(nptr);
using namespace std;
// Printing the memory addresses and
int main() values
cout << "Integer Address: " << nptr <<
{
endl;
int n = 10; cout << "Char Address: " << charptr;
// Store the address of number return 0;
in nptr }
Output:
int* nptr = &n;
Integer Address: 0x7ffcd595d6fc
Char Address: 0x7ffcd595d6fc
Note:
• If two different classes have functions with the same signature,
casting their function pointers can cause undefined behavior - Casting
Between Member Functions of Different Classes (Dangerous)
• Same Class → Safe Casting (if function signatures match).
• Base to Derived Class → Safe with static_cast.
• Unrelated Classes → Never Use reinterpret_cast (leads to undefined
behavior
Virtual Functions
• A member function declared as 'virtual' in the base class.
• Allows derived class to override it.
• Enables dynamic binding at runtime.
• Provides polymorphic behavior.
Example - Virtual Function
#include <iostream>
using namespace std;
class Base {
public:
virtual void show() { // Virtual function
cout << "Base class show function" << endl;
}
};
class Derived : public Base {
public:
void show() override { // Overriding the base class function
cout << "Derived class show function" << endl;
}
};
int main() {
Base* bptr;
Derived d;
bptr = &d;
bptr->show(); // Calls Derived class function due to dynamic binding
return 0;
}
Virtual Destructors
• Ensures proper cleanup of derived class objects.
• Prevents memory leaks when deleting objects through base class
pointers.
• Declared using 'virtual ~ClassName();' in the base class.
• In short, A virtual destructor ensures that when an object is deleted
through a base class pointer, the destructor of the derived class is also
called properly, preventing memory leaks.
Example - Virtual Destructor
#include <iostream>
using namespace std;
class Base {
public:
Base() { cout << "Base Constructor" << endl; }
virtual ~Base() { cout << "Base Destructor" << endl; }
};
class Derived : public Base {
public:
Derived() { cout << "Derived Constructor" << endl; }
~Derived() { cout << "Derived Destructor" << endl; }
};
int main() {
Base* b = new Derived();
delete b; // Calls both base and derived destructors correctly
return 0;
}
Output
Base Constructor
Derived Constructor
Derived Destructor
Base Destructor
• If the destructor was not virtual, only Base Destructor would be
called, causing a memory leak.
Dynamic Binding
• Dynamic binding occurs when function calls are resolved at runtime
rather than compile time.
• It happens automatically when a virtual function is called through a
base class pointer/reference.
• Ensures the correct overridden function is executed.
Example - Dynamic Binding
#include <iostream>
using namespace std;
class Animal {
public:
virtual void sound() { cout << "Animal makes a sound" << endl; }
};
class Dog : public Animal {
public:
void sound() override { cout << "Dog barks" << endl; }
};
class Cat : public Animal {
public:
void sound() override { cout << "Cat meows" << endl; }
};
int main() {
Animal* a;
Dog d;
Cat c;
a = &d;
a->sound(); // Calls Dog's sound() due to dynamic binding
a = &c;
a->sound(); // Calls Cat's sound() due to dynamic binding
return 0;
}
Output
Dog barks
Cat meows
• How Does Function call resolved at runtime?
1. Virtual Table (vtable):The compiler creates a table of function pointers
(vtable) for classes with virtual functions.
2. Virtual Pointer (vptr):Every object of such a class contains a hidden
pointer to its class's vtable.
3. Function Call at Runtime: When a pointer points to a function is called,
the vptr directs the call to the correct function from the vtable based on
the actual object.