OOPs in C++
A Simple Guide
Pawanpreet Singh
www.digitalcollegelibrary.com
Index
Chapter – 1 Classes and Objects
1. Classes
2. Objects
Chapter – 2 Encapsulation
1. Definition of Encapsulation
2. Access Specifiers in Encapsulation
3. Benefits of Encapsulation
4. Getter and Setter Encapsulation
Chapter – 3 Abstraction
1. Definition of Abstraction
2. Abstract Class
Chapter – 4 Inheritance
1. Definition of Inheritance
2. Types of Inheritance
3. Access Specifiers in Inheritance
4. Function Overriding in Inheritance
Chapter – 5 Polymorphism
1. Definition of Polymorphism
2. Types of Polymorphism
3. Compile Time Polymorphism
4. Run Time Polymorphism
Chapter – 6 Constructor and Destructor
1. Definition of Constructor and Destructor
2. Importance of Constructor and Destructor
3. Types of Constructor
4. Destructor
Chapter – 7 Operator Overloading
1. Definition of Operator Overloading
2. Why Operator Overloading ?
3. How Operator Overloading Works
4. Common Operators that can be Overload
5. Example of Operator Overloading
Chapter – 8 Access Specifiers & Data Hiding
1. Definition of Access Specifiers
2. Public Access Specifiers
3. Private Access Specifiers
4. Protected Access Specifiers
5. Data Hiding
Chapter – 9 Friend Function & Friend Class
1. Introduction
2. Friend Function
3. Friend Class
Chapter – 10 Static Members
1. Introduction to Static Members
2. Static Variables
3. Static Function
Chapter – 11 this Pointer
1. Introduction to this Pointer
2. Definition and Behavior
3. Use Cases of this Pointer
Chapter – 12 Type Casting
1. Introduction to Type Casting
2. Static Casting
3. Dynamic Casting
4. Const Casting
5. Reinterpret Casting
Chapter -1
Classes and Objects
In C++, Object-Oriented Programming (OOP) is centered around
organizing data and behavior into logical structures called classes
and objects. Classes provide the structure and behavior, serving
as blueprints, while objects are the actual instances based on
these classes. Together, they encapsulate data and functionality,
making code more modular, reusable, and easier to understand.
Let's delve deeply into what classes and objects are, how they
work, and how you can effectively use them in C++.
1.1 Classes in C++
A class in C++ is essentially a template or blueprint used to create
objects. It encapsulates data and behaviors (or functions) that
operate on that data. Classes promote data encapsulation and
abstraction, which help organize complex systems by dividing
them into smaller, manageable units.
Syntax for Declaring a Class:
class ClassName {
public:
// Public members (accessible from outside the class)
int publicData;
void publicFunction();
private:
www.digitalcollegelibrary.com
// Private members (only accessible within the class)
int privateData;
void privateFunction();
};
In this structure:
ClassName is the name of the class.
The keyword public denotes that members under this section
can be accessed from outside the class.
private members are restricted to being accessed only within
the class itself.
Example: Defining a Simple Class
Let's look at a simple example. Suppose we want to represent a
Book in a library system:
#include <iostream>
#include <string>
using namespace std;
class Book {
public:
// Public attributes
string title;
string author;
int pages;
www.digitalcollegelibrary.com
// Constructor
Book(string t, string a, int p) {
title = t;
author = a;
pages = p;
// Public method
void displayInfo() {
cout << "Title: " << title << endl;
cout << "Author: " << author << endl;
cout << "Pages: " << pages << endl;
};
In this example:
Attributes: title, author, and pages are attributes of the Book
class. These hold data specific to a particular book instance.
Constructor: Book(string t, string a, int p) is a constructor that
initializes an object with a title, author, and page count.
Method: displayInfo() is a method that prints the book details.
1.2 Objects in C++
An object is an instance of a class. When you create an object,
memory is allocated, and it can then hold values that represent a
specific example of the class template.
www.digitalcollegelibrary.com
Creating and Using Objects
Once a class is defined, you can create objects of that class:
int main() {
// Creating an object of the Book class
Book myBook("The Great Gatsby", "F. Scott Fitzgerald", 218);
// Accessing the public method of the class using the object
myBook.displayInfo();
return 0;
Output:
Title: The Great Gatsby
Author: F. Scott Fitzgerald
Pages: 218
In this example:
Object Creation: Book myBook("The Great Gatsby", "F. Scott
Fitzgerald", 218); creates an instance myBook of the Book
class.
Method Call: myBook.displayInfo(); calls the displayInfo
method using the object to print the book's details.
Each object created from a class has its own set of attributes,
independent of other objects, though all instances share the same
class structure.
www.digitalcollegelibrary.com
Chapter -2
Encapsulation
2.1 Definition of Encapsulation
Encapsulation is the technique of wrapping or bundling data and
the functions that manipulate that data into a single unit, called a
class. In C++, a class defines the structure of data and the
behavior for manipulating that data. By encapsulating attributes
and methods, a class keeps data organized, ensures the
functionality related to that data is logically grouped, and
enforces access restrictions.
In encapsulation:
Data Members (or attributes) represent the state or
properties of the class.
Member Functions (or methods) operate on the data and
provide controlled access to it.
Why Use Encapsulation?
The main reason for encapsulation is to hide the internal
representation of an object from the outside. Instead of directly
accessing and manipulating the data, encapsulation allows
external code to interact with an object through well-defined
interfaces (public methods). This approach protects the data from
unintended changes, keeps it in a valid state, and reduces
dependencies between different parts of the program.
Example of Encapsulation in C++
Consider a simple example of a BankAccount class where we
encapsulate the balance and provide methods to deposit,
withdraw, and get the current balance.
www.digitalcollegelibrary.com
#include <iostream>
using namespace std;
class BankAccount {
private:
double balance; // Private attribute
public:
// Constructor to initialize balance
BankAccount(double initialBalance) {
if (initialBalance >= 0)
balance = initialBalance;
else
balance = 0;
// Public method to deposit amount
void deposit(double amount) {
if (amount > 0)
balance += amount;
www.digitalcollegelibrary.com
// Public method to withdraw amount
void withdraw(double amount) {
if (amount > 0 && amount <= balance)
balance -= amount;
// Public method to get balance
double getBalance() const {
return balance;
};
int main() {
BankAccount account(1000.0);
account.deposit(500);
account.withdraw(200);
cout << "Current balance: $" << account.getBalance() << endl;
return 0;
In this example:
Data Encapsulation: The balance attribute is private, so it’s
hidden from direct access by outside code.
www.digitalcollegelibrary.com
Controlled Access: Public methods (deposit, withdraw, and
getBalance) control how the balance attribute is accessed
and modified.
By encapsulating data within the class and exposing only
necessary methods, we maintain control over the state of the
balance, ensuring that deposits and withdrawals are handled in a
controlled manner.
2.2 Access Specifiers in Encapsulation
C++ uses access specifiers to control visibility and access to the
members of a class. There are three main access specifiers:
1. Public
Members declared as public are accessible from outside the
class.
Typically used for methods that need to be accessed by
other classes or functions in the program.
public:
void displayBalance() {
cout << "Balance: $" << balance << endl;
2. Private
Members declared as private are only accessible within the
class itself.
They cannot be accessed directly from outside the class.
Often used for data members that need protection from
unauthorized access or modification.
private:
double balance; // Balance is private and cannot be
accessed directly
www.digitalcollegelibrary.com
3. Protected
Members declared as protected are accessible within the
class and by derived (inherited) classes.
Often used in inheritance scenarios, where specific attributes
need to be accessible to subclasses but hidden from other
code.
protected:
double interestRate; // Example of protected member
In the BankAccount example, balance is private, so it’s
inaccessible outside of BankAccount. This restriction enforces data
hiding and keeps balance secure.
2.3 Benefits of Encapsulation
Encapsulation provides several key benefits in C++:
Data Hiding
By making sensitive data private, encapsulation hides the
data from direct external access, protecting it from
unauthorized changes.
Only functions inside the class have direct access to private
members, enforcing controlled and predictable data
manipulation.
Data Integrity
Encapsulation helps maintain the integrity of data by
allowing controlled access through specific methods. These
methods can include error-checking logic to ensure the data
remains valid.
For example, in the BankAccount class, negative deposits
and withdrawals greater than the balance are prevented,
thus ensuring valid account operations.
www.digitalcollegelibrary.com
Code Organization and Modularity
Encapsulation logically groups data and methods that
operate on the data within a single unit (class), making the
code easier to understand and maintain.
This separation of concerns allows developers to work on
different classes independently, enhancing modularity.
Flexibility and Reusability
Well-encapsulated classes can be reused across different
programs and projects because they provide a clear
interface.
Internal changes to data and methods can be made
without affecting other parts of the program that rely on the
class’s interface.
2.4 Getter and Setters Encapsulation
Getters and setters are special methods that provide controlled
access to private data members. They are also known as
accessor and mutator methods, respectively.
Purpose of Getters and Setters
Getters allow external code to read the value of a private
attribute without exposing the attribute itself.
Setters allow external code to modify the value of a private
attribute while validating or restricting the values assigned to
it.
Example of Getters and Setters
Consider a Person class with a private age attribute. We’ll use a
getter to retrieve the age and a setter to validate and modify it.
#include <iostream>
using namespace std;
www.digitalcollegelibrary.com
class Person {
private:
int age;
public:
// Setter method with validation
void setAge(int a) {
if (a >= 0 && a <= 120) { // Valid age range
age = a;
} else {
cout << "Invalid age value." << endl;
// Getter method
int getAge() const {
return age;
};
int main() {
www.digitalcollegelibrary.com
Person person;
person.setAge(25); // Valid age
cout << "Person's age: " << person.getAge() << endl;
person.setAge(-5); // Invalid age, triggers validation
return 0;
Output:
Person's age: 25
Invalid age value.
In this example:
Data Validation: The setAge method includes validation
logic, ensuring that age values are within a realistic range.
Data Access: The getAge method provides controlled
access to the age attribute without exposing it directly.
Advantages of Using Getters and Setters
1. Data Control: Setters allow for validation, restricting invalid
values from being assigned to the data member.
2. Flexibility: By modifying the getter and setter methods, the
class’s internal implementation can change without
affecting the external interface.
3. Read-Only or Write-Only Access: Getters and setters can be
designed to make an attribute read-only (getter only) or
write-only (setter only).
www.digitalcollegelibrary.com
Chapter -3
Abstraction
3.1 Definition of Abstraction
Abstraction is the process of exposing only the necessary parts of
an object while hiding its internal complexity. In essence,
abstraction provides a simplified view of an object, allowing
developers to focus on what an object does rather than how it
does it. In C++, abstraction is commonly achieved using:
Abstract classes: Classes that cannot be instantiated directly
and often serve as blueprints for other classes.
Interfaces: Collections of pure virtual functions in abstract
classes that define a common structure for derived classes.
Example of Abstraction: A car dashboard abstracts away the
complexity of the engine and transmission systems by providing
simple interfaces like the steering wheel, gas pedal, and brakes.
You can drive the car without understanding the intricate details
of how the engine works.
3.2 Abstract Class
An abstract class in C++ is a class that is designed to be a base
class for other classes. It contains at least one pure virtual function.
A pure virtual function is a function declared with the = 0 syntax,
which forces derived classes to provide an implementation for it.
Abstract classes cannot be instantiated directly because they
contain functions without implementations. Instead, they act as a
blueprint for other classes, which inherit from them and provide
implementations for the abstract methods.
www.digitalcollegelibrary.com
Syntax for Declaring an Abstract Class
Here’s how to declare an abstract class with a pure virtual
function in C++:
class Shape {
public:
// Pure virtual function - must be implemented by derived
classes
virtual void draw() = 0;
};
In this example:
Shape is an abstract class because it contains a pure virtual
function draw().
The = 0 syntax after the function declaration indicates that
draw() has no implementation in Shape.
Creating Derived Classes from an Abstract Class
Let’s create two derived classes, Circle and Rectangle, that inherit
from the Shape abstract class and provide implementations for
the draw() function.
#include <iostream>
using namespace std;
class Shape {
public:
virtual void draw() = 0; // Pure virtual function
www.digitalcollegelibrary.com
};
class Circle : public Shape {
public:
void draw() override {
cout << "Drawing Circle" << endl;
};
class Rectangle : public Shape {
public:
void draw() override {
cout << "Drawing Rectangle" << endl;
};
int main() {
// We cannot instantiate Shape directly
// Shape shape; // This will cause a compile error
Shape* shape1 = new Circle();
Shape* shape2 = new Rectangle();
www.digitalcollegelibrary.com
shape1->draw(); // Output: Drawing Circle
shape2->draw(); // Output: Drawing Rectangle
delete shape1;
delete shape2;
return 0;
Output:
Drawing Circle
Drawing Rectangle
In this example:
Polymorphism is used to create a common interface for
Circle and Rectangle objects.
Shape* shape1 = new Circle(); and Shape* shape2 = new
Rectangle(); demonstrate how abstract classes support
polymorphic behavior, allowing different implementations
(Circle and Rectangle) to be accessed through the same
interface (Shape).
www.digitalcollegelibrary.com
Chapter -4
Inheritance
4.1 Definition of Inheritance
Inheritance is an OOP concept that enables a class (known as the
derived class) to inherit properties and behavior from another
class (the base class). This mechanism promotes code reuse by
allowing derived classes to share and reuse code from base
classes, rather than rewriting the same functionality.
For example, if we have a base class Animal, we can derive
specific classes like Dog, Cat, and Bird from it. Each derived class
inherits the common features from Animal but can also introduce
specific behaviors of its own.
Why Use Inheritance?
Code Reusability: By reusing existing code in the base class,
derived classes can avoid redundancy.
Hierarchical Class Structures: Inheritance allows for a
structured hierarchy of classes that are easy to navigate and
extend.
Flexibility and Extensibility: New functionality can be added
without modifying existing classes.
Polymorphism: Inheritance allows for polymorphic behavior,
where derived classes can define their own versions of base
class methods.
4.2 Types of Inheritance
C++ supports several types of inheritance, each suited for different
purposes and hierarchical structures.
www.digitalcollegelibrary.com
1. Single Inheritance
In single inheritance, a derived class inherits from only one base
class. This is the simplest form of inheritance, where the derived
class has a one-to-one relationship with its base class.
#include <iostream>
using namespace std;
class Animal {
public:
void eat() {
cout << "Animal eats food." << endl;
};
class Dog : public Animal {
public:
void bark() {
cout << "Dog barks." << endl;
};
int main() {
Dog myDog;
www.digitalcollegelibrary.com
myDog.eat(); // Inherited from Animal
myDog.bark(); // Defined in Dog
return 0;
Output:
Animal eats food.
Dog barks.
In this example:
Dog is a derived class that inherits from Animal.
Dog can access Animal's eat() function through inheritance,
while also defining its own behavior with bark().
2. Multiple Inheritance
In multiple inheritance, a derived class inherits from more than one
base class. This type of inheritance allows a class to combine
features from multiple base classes.
Example of Multiple Inheritance:
#include <iostream>
using namespace std;
class Animal {
public:
void eat() {
cout << "Animal eats food." << endl;
www.digitalcollegelibrary.com
}
};
class Bird {
public:
void fly() {
cout << "Bird flies." << endl;
};
class Bat : public Animal, public Bird {
public:
void sound() {
cout << "Bat makes sound." << endl;
};
int main() {
Bat bat;
bat.eat(); // Inherited from Animal
bat.fly(); // Inherited from Bird
bat.sound(); // Defined in Bat
www.digitalcollegelibrary.com
return 0;
Output:
Animal eats food.
Bird flies.
Bat makes sound.
Here:
Bat inherits from both Animal and Bird, gaining access to
both eat() and fly() methods.
Note: Multiple inheritance can lead to complexity,
particularly in the case of the diamond problem (explained
later). To manage this, C++ offers solutions like virtual
inheritance.
3. Multilevel Inheritance
In multilevel inheritance, a class is derived from another derived
class, creating multiple levels of inheritance.
Example of Multilevel Inheritance:
#include <iostream>
using namespace std;
class Animal {
public:
void breathe() {
cout << "Animal breathes." << endl;
www.digitalcollegelibrary.com
}
};
class Mammal : public Animal {
public:
void walk() {
cout << "Mammal walks." << endl;
};
class Dog : public Mammal {
public:
void bark() {
cout << "Dog barks." << endl;
};
int main() {
Dog myDog;
myDog.breathe(); // Inherited from Animal
myDog.walk(); // Inherited from Mammal
myDog.bark(); // Defined in Dog
www.digitalcollegelibrary.com
return 0;
Output:
Animal breathes.
Mammal walks.
Dog barks.
In this example:
Dog inherits from Mammal, which in turn inherits from Animal,
forming a multilevel inheritance chain.
Dog has access to methods in both Mammal and Animal.
4. Hierarchical Inheritance
In hierarchical inheritance, multiple derived classes inherit from a
single base class. This structure is useful when several classes share
common features from a single base class.
Example of Hierarchical Inheritance:
#include <iostream>
using namespace std;
class Animal {
public:
void eat() {
cout << "Animal eats food." << endl;
www.digitalcollegelibrary.com
};
class Dog : public Animal {
public:
void bark() {
cout << "Dog barks." << endl;
};
class Cat : public Animal {
public:
void meow() {
cout << "Cat meows." << endl;
};
int main() {
Dog myDog;
Cat myCat;
myDog.eat(); // Inherited from Animal
myDog.bark();
www.digitalcollegelibrary.com
myCat.eat(); // Inherited from Animal
myCat.meow();
return 0;
Output:
Animal eats food.
Dog barks.
Animal eats food.
Cat meows.
In this example:
Both Dog and Cat inherit from Animal, giving them access to
the eat() function.
Each derived class (Dog and Cat) has its own unique
behavior.
4.3 Access Specifies in Inheritance
In C++, access specifiers play an essential role in inheritance. The
base class’s access specifier influences the visibility of its members
in the derived class. C++ provides three main types of inheritance
based on access specifiers:
1. Public Inheritance: Public members of the base class remain
public in the derived class, and protected members stay
protected.
2. Protected Inheritance: Public and protected members of the
base class become protected in the derived class.
www.digitalcollegelibrary.com
3. Private Inheritance: Public and protected members of the
base class become private in the derived class.
Example of Access Specifiers in Inheritance
#include <iostream>
using namespace std;
class Base {
public:
int publicVar;
protected:
int protectedVar;
private:
int privateVar;
};
class PublicDerived : public Base {
public:
void accessMembers() {
publicVar = 1; // Accessible
protectedVar = 2; // Accessible
// privateVar = 3; // Not accessible
};
www.digitalcollegelibrary.com
class PrivateDerived : private Base {
public:
void accessMembers() {
publicVar = 1; // Accessible as private
protectedVar = 2; // Accessible as private
};
int main() {
PublicDerived pub;
pub.publicVar = 10; // Accessible
// pub.protectedVar = 20; // Not accessible outside the class
PrivateDerived priv;
// priv.publicVar = 10; // Not accessible outside the class due to
private inheritance
return 0;
4.4 Function Overriding in Inheritance
Function overriding occurs when a derived class defines a method
that has the same name, return type, and parameters as a
method in its base class. This allows the derived class to provide a
specific implementation for the method.
www.digitalcollegelibrary.com
To override a function, the base class function must be virtual, and
the derived class should use the override keyword for clarity.
#include <iostream>
using namespace std;
class Animal {
public:
virtual void sound() {
cout << "Animal sound" << endl;
};
class Dog : public Animal {
public:
void sound() override {
cout << "Dog barks" << endl;
};
int main() {
Animal* animal = new Dog();
animal->sound(); // Output: Dog barks
delete animal;
www.digitalcollegelibrary.com
return 0;
Output:
Dog barks
In this example, polymorphism allows the sound() method in Dog
to override Animal's sound() function, resulting in specific behavior
when the function is called on a Dog object.
www.digitalcollegelibrary.com
Chapter -5
Polymorphism
5.1 Definition of Polymorphism
Polymorphism in C++ is the ability of a function, operator, or
object to perform different tasks based on the context. By
providing a common interface for objects of different classes,
polymorphism enables code that can work with objects of
multiple types and reduces the need for duplicate code.
5.2 Types of Polymorphism
C++ supports two primary types of polymorphism:
1. Compile-Time (Static) Polymorphism: The behavior is resolved
at compile time. This type of polymorphism is achieved using
function overloading and operator overloading.
2. Run-Time (Dynamic) Polymorphism: The behavior is
determined at runtime, often using inheritance and virtual
functions. This type of polymorphism is achieved using virtual
functions and abstract classes.
5.3 Compile Time Polymorphism
In compile-time polymorphism, the function to be invoked or the
operator to be used is determined during compilation. This is
typically faster than dynamic polymorphism as there is no runtime
decision-making involved. Static polymorphism is implemented
using function overloading and operator overloading.
Function Overloading
www.digitalcollegelibrary.com
Function overloading is a type of polymorphism where multiple
functions have the same name but differ in the number or type of
parameters. The compiler decides which function to invoke based
on the arguments provided. This is helpful when you want similar
functions that perform similar operations but with different inputs.
Syntax and Example:
#include <iostream>
using namespace std;
class Print {
public:
void display(int i) {
cout << "Displaying integer: " << i << endl;
void display(double d) {
cout << "Displaying double: " << d << endl;
void display(string str) {
cout << "Displaying string: " << str << endl;
};
www.digitalcollegelibrary.com
int main() {
Print printObj;
printObj.display(10); // Calls display(int)
printObj.display(5.5); // Calls display(double)
printObj.display("Hello"); // Calls display(string)
return 0;
Output:
Displaying integer: 10
Displaying double: 5.5
Displaying string: Hello
In this example:
Function Overloading is achieved by defining multiple
versions of the display() function that take different types of
arguments.
The compiler decides which function to call based on the
argument types provided.
Operator Overloading
Operator overloading allows operators to be redefined and used
with user-defined data types. By overloading operators, we can
provide specific implementations for how operators work with
objects of custom classes, enhancing the usability and readability
of those classes.
Syntax and Example:
#include <iostream>
www.digitalcollegelibrary.com
using namespace std;
class Complex {
private:
float real, imag;
public:
Complex(float r = 0, float i = 0) : real(r), imag(i) {}
// Overloading the + operator
Complex operator + (const Complex& other) {
return Complex(real + other.real, imag + other.imag);
void display() const {
cout << real << " + " << imag << "i" << endl;
};
int main() {
Complex c1(3.3, 4.4), c2(1.1, 2.2);
Complex c3 = c1 + c2; // Calls overloaded + operator
c3.display(); // Output: 4.4 + 6.6i
www.digitalcollegelibrary.com
return 0;
Output:
4.4 + 6.6i
In this example:
Operator Overloading is used to redefine the + operator for
the Complex class, allowing Complex objects to be added
using natural syntax.
The function operator+ enables adding two complex
numbers, returning a new Complex object with the sum.
5.4 Run Time Polymorphism
In dynamic polymorphism, the function to be executed is
determined at runtime based on the actual type of the object.
This is achieved through virtual functions and abstract classes in
C++, which support dynamic method binding.
Virtual Functions
A virtual function is a function in a base class that is intended to be
overridden in derived classes. When a base class pointer refers to
a derived class object, calling a virtual function invokes the
derived class’s version, thanks to a mechanism called dynamic
binding or late binding.
To declare a function as virtual, use the virtual keyword in the base
class. This enables derived classes to provide their specific
implementation.
Syntax and Example:
#include <iostream>
using namespace std;
www.digitalcollegelibrary.com
class Animal {
public:
virtual void sound() { // Virtual function
cout << "Animal makes a sound." << endl;
};
class Dog : public Animal {
public:
void sound() override { // Overrides base class sound()
cout << "Dog barks." << endl;
};
class Cat : public Animal {
public:
void sound() override { // Overrides base class sound()
cout << "Cat meows." << endl;
};
www.digitalcollegelibrary.com
int main() {
Animal* animal1 = new Dog();
Animal* animal2 = new Cat();
animal1->sound(); // Output: Dog barks.
animal2->sound(); // Output: Cat meows.
delete animal1;
delete animal2;
return 0;
Output:
Dog barks.
Cat meows.
In this example:
sound() is declared as a virtual function in the Animal base
class.
When calling sound() through a base class pointer, the
appropriate derived class function (Dog or Cat) is invoked at
runtime.
Pure Virtual Functions and Abstract Classes
A pure virtual function is a virtual function that has no
implementation in the base class. It is declared by assigning = 0 to
the function prototype. Classes that contain pure virtual functions
www.digitalcollegelibrary.com
are called abstract classes and cannot be instantiated directly.
Abstract classes serve as blueprints for derived classes, which must
implement the pure virtual functions.
Syntax and Example:
#include <iostream>
using namespace std;
class Shape {
public:
virtual void draw() = 0; // Pure virtual function
};
class Circle : public Shape {
public:
void draw() override {
cout << "Drawing Circle" << endl;
};
class Rectangle : public Shape {
public:
void draw() override {
cout << "Drawing Rectangle" << endl;
www.digitalcollegelibrary.com
}
};
int main() {
Shape* shape1 = new Circle();
Shape* shape2 = new Rectangle();
shape1->draw(); // Output: Drawing Circle
shape2->draw(); // Output: Drawing Rectangle
delete shape1;
delete shape2;
return 0;
Output:
Drawing Circle
Drawing Rectangle
In this example:
Shape is an abstract class with a pure virtual function draw().
Both Circle and Rectangle provide specific implementations
of draw().
www.digitalcollegelibrary.com
Chapter -6
Constructor and Destructor
6.1 Definition of Constructor and Destructor
Constructor: A constructor is a special member function that is
automatically called when an object of a class is created. Its
primary purpose is to initialize the object's attributes and allocate
resources as needed. Constructors have the same name as the
class and do not have a return type, not even void.
Destructor: A destructor is another special member function that is
automatically called when an object goes out of scope or is
explicitly deleted. Its main purpose is to perform cleanup tasks,
such as deallocating memory and releasing resources that were
allocated during the object's lifetime. A destructor has the same
name as the class but is preceded by a tilde (~) symbol.
6.2 Importance of Constructor and Destructor
Constructors and destructors are essential for:
Resource Management: They ensure that resources are
allocated and deallocated properly, preventing memory
leaks and dangling pointers.
Object Initialization: Constructors allow for the automatic
initialization of object attributes, ensuring that objects start in
a valid state.
Encapsulation: They promote encapsulation by hiding the
implementation details of resource management within the
class.
Consistency: They provide a consistent interface for object
creation and destruction.
www.digitalcollegelibrary.com
6.3 Types of Constructor
C++ supports three primary types of constructors:
A. Default Constructor
A default constructor is a constructor that takes no parameters. It
initializes the object with default values. If no constructor is
explicitly defined, C++ will provide a default constructor
automatically.
Example of Default Constructor:
#include <iostream>
using namespace std;
class Box {
public:
int length;
int width;
int height;
// Default constructor
Box() {
length = 1;
width = 1;
height = 1;
cout << "Default constructor called!" << endl;
www.digitalcollegelibrary.com
}
};
int main() {
Box box1; // Calls the default constructor
cout << "Length: " << box1.length << ", Width: " << box1.width <<
", Height: " << box1.height << endl;
return 0;
Output:
Default constructor called!
Length: 1, Width: 1, Height: 1
In this example, the Box class has a default constructor that
initializes the dimensions to 1. When an object of Box is created,
the default constructor is called automatically.
B. Parameterized Constructor
A parameterized constructor takes one or more parameters and
allows the initialization of object attributes with specific values
provided at the time of object creation.
Example of Parameterized Constructor:
#include <iostream>
using namespace std;
class Box {
www.digitalcollegelibrary.com
public:
int length;
int width;
int height;
// Parameterized constructor
Box(int l, int w, int h) {
length = l;
width = w;
height = h;
cout << "Parameterized constructor called!" << endl;
};
int main() {
Box box1(5, 6, 7); // Calls the parameterized constructor
cout << "Length: " << box1.length << ", Width: " << box1.width <<
", Height: " << box1.height << endl;
return 0;
Output:
Parameterized constructor called!
Length: 5, Width: 6, Height: 7
www.digitalcollegelibrary.com
In this example, the Box class has a parameterized constructor
that initializes the dimensions with specific values passed as
arguments. When box1 is created, the parameterized constructor
is invoked.
C. Copy Constructor
A copy constructor is a special constructor that initializes a new
object as a copy of an existing object. It is used when an object is
passed by value, returned from a function, or initialized with
another object of the same class.
Example of Copy Constructor:
#include <iostream>
using namespace std;
class Box {
public:
int length;
int width;
int height;
// Parameterized constructor
Box(int l, int w, int h) : length(l), width(w), height(h) {}
// Copy constructor
Box(const Box &b) {
www.digitalcollegelibrary.com
length = b.length;
width = b.width;
height = b.height;
cout << "Copy constructor called!" << endl;
};
int main() {
Box box1(5, 6, 7); // Calls parameterized constructor
Box box2 = box1; // Calls copy constructor
cout << "Box1 Length: " << box1.length << ", Width: " <<
box1.width << ", Height: " << box1.height << endl;
cout << "Box2 Length: " << box2.length << ", Width: " <<
box2.width << ", Height: " << box2.height << endl;
return 0;
Output:
Copy constructor called!
Box1 Length: 5, Width: 6, Height: 7
Box2 Length: 5, Width: 6, Height: 7
www.digitalcollegelibrary.com
In this example, Box has a copy constructor that copies the
dimensions from another Box object. When box2 is created as a
copy of box1, the copy constructor is called.
6.4 Destructor
A destructor is a special member function that is called when an
object is destroyed. Its primary purpose is to perform cleanup
tasks, such as deallocating memory or releasing resources that
were allocated during the object's lifetime. The destructor has the
same name as the class, preceded by a tilde (~), and cannot
take parameters or return values.
Example of Destructor:
#include <iostream>
using namespace std;
class Box {
public:
int length;
int width;
int height;
// Constructor
Box(int l, int w, int h) : length(l), width(w), height(h) {
cout << "Constructor called!" << endl;
www.digitalcollegelibrary.com
// Destructor
~Box() {
cout << "Destructor called!" << endl;
};
int main() {
Box box1(5, 6, 7); // Calls the constructor
// Destructor is called automatically when box1 goes out of
scope
return 0;
Output:
Constructor called!
Destructor called!
In this example, the destructor is called automatically when box1
goes out of scope at the end of the main() function. This ensures
that any cleanup operations defined in the destructor are
performed.
www.digitalcollegelibrary.com
Chapter -7
Operator Overloading
7.1 Definition of Operator Overloading
Operator Overloading is the process of providing a specific
implementation of an operator for user-defined data types (such
as classes). In C++, operators such as +, -, *, and / can be
redefined to operate on instances of classes, allowing for seamless
integration of user-defined types in expressions.
This is done by defining a function, known as an operator function,
that specifies how the operator behaves when applied to the
user-defined types.
7.1 Why Operator Overloading ?
Operator overloading is useful for several reasons:
Intuitive Code: It allows developers to use operators in a way
that is intuitive and matches the semantics of the objects
being manipulated. For example, adding two complex
numbers using the + operator is more natural than using a
method call like add().
Code Readability: Code that uses overloaded operators can
be cleaner and easier to read, making it more maintainable.
Enhanced Functionality: It provides a way to enhance the
functionality of classes without changing their interface.
Operators can be overloaded to provide additional features
for classes while still adhering to the expected behavior of
those operators.
www.digitalcollegelibrary.com
7.3 How Operator Overloading works
Syntax of Operator Overloading
To overload an operator, you define a special member function or
a friend function that uses the keyword operator followed by the
symbol of the operator being overloaded.
General Syntax:
ReturnType operator OperatorSymbol (ParameterList) {
// Implementation
ReturnType: The type of value returned by the operator
function.
OperatorSymbol: The operator to be overloaded (e.g., +, -, *,
etc.).
ParameterList: The parameters passed to the operator
function, which can include objects of the class being
defined or other types.
Example Syntax
class Complex {
public:
float real;
float imag;
// Overloading the + operator
Complex operator+(const Complex &other) {
Complex result;
result.real = real + other.real;
www.digitalcollegelibrary.com
result.imag = imag + other.imag;
return result;
};
Rules for Overloading Operators
1. Cannot Change Operator Precedence: The precedence and
associativity of operators cannot be changed.
2. Cannot Overload Certain Operators: Some operators, such
as ::, . (dot), .*, and ?:, cannot be overloaded.
3. Must Have at Least One User-Defined Type: At least one of
the operands must be of a user-defined type.
4. Function Name: The name of the function must start with the
keyword operator followed by the operator symbol.
5. Return Type: The return type can be any valid type, including
user-defined types.
7.4 Common Operators that can be overload
Many operators can be overloaded in C++, including but not
limited to:
Arithmetic Operators: +, -, *, /, %
Comparison Operators: ==, !=, <, >, <=, >=
Increment/Decrement Operators: ++, --
Assignment Operator: =
Input/Output Operators: <<, >>
Subscript Operator: []
Function Call Operator: ()
7.5 Examples of Operator Overloading
A. Overloading Arithmetic Operators
www.digitalcollegelibrary.com
Let's create a class to represent complex numbers and overload
the + operator to add two complex numbers.
#include <iostream>
using namespace std;
class Complex {
private:
float real;
float imag;
public:
// Constructor
Complex(float r = 0, float i = 0) : real(r), imag(i) {}
// Overloading the + operator
Complex operator+(const Complex &other) {
return Complex(real + other.real, imag + other.imag);
// Display function
void display() const {
cout << real << " + " << imag << "i" << endl;
www.digitalcollegelibrary.com
};
int main() {
Complex c1(3.5, 2.5);
Complex c2(1.5, 3.5);
Complex c3 = c1 + c2; // Uses overloaded + operator
c3.display(); // Output: 5 + 6i
return 0;
Output:
5 + 6i
In this example, the + operator is overloaded to enable addition
of two Complex objects, resulting in a new Complex object.
B. Overloading Comparison Operators
Next, let's overload the == operator to compare two complex
numbers.
#include <iostream>
using namespace std;
class Complex {
www.digitalcollegelibrary.com
private:
float real;
float imag;
public:
// Constructor
Complex(float r = 0, float i = 0) : real(r), imag(i) {}
// Overloading the == operator
bool operator==(const Complex &other) const {
return (real == other.real) && (imag == other.imag);
// Display function
void display() const {
cout << real << " + " << imag << "i" << endl;
};
int main() {
Complex c1(3.5, 2.5);
Complex c2(3.5, 2.5);
www.digitalcollegelibrary.com
if (c1 == c2) { // Uses overloaded == operator
cout << "c1 is equal to c2." << endl;
} else {
cout << "c1 is not equal to c2." << endl;
return 0;
Output:
c1 is equal to c2.
Here, we overloaded the == operator to compare the real and
imag parts of two Complex objects.
C. Overloading Increment and Decrement Operators
We can also overload the increment (++) operator to increase the
values of a complex number.
#include <iostream>
using namespace std;
class Complex {
private:
float real;
www.digitalcollegelibrary.com
float imag;
public:
// Constructor
Complex(float r = 0, float i = 0) : real(r), imag(i) {}
// Overloading the ++ operator (prefix)
Complex operator++() {
++real; // Increment real part
return *this;
// Display function
void display() const {
cout << real << " + " << imag << "i" << endl;
};
int main() {
Complex c(3.5, 2.5);
++c; // Uses overloaded ++ operator
www.digitalcollegelibrary.com
c.display(); // Output: 4.5 + 2.5i
return 0;
Output:
4.5 + 2.5i
In this example, we overloaded the prefix increment operator (++)
to increment the real part of the complex number.
D. Overloading I/O Operators
Overloading the stream insertion (<<) operator allows us to print
complex numbers directly using cout.
#include <iostream>
using namespace std;
class Complex {
private:
float real;
float imag;
public:
// Constructor
Complex(float r = 0, float i = 0) : real(r), imag(i) {}
www.digitalcollegelibrary.com
// Overloading the << operator for output
friend ostream& operator<<(ostream &out, const Complex &c) {
out << c.real << " + " << c.imag << "i";
return out;
};
int main() {
Complex c(3.5, 2.5);
cout << "Complex number: " << c << endl; // Uses overloaded
<< operator
return 0;
Output:
Complex number: 3.5 + 2.5i
In this example, the << operator is overloaded to provide a
custom way of outputting the Complex objects to the standard
output stream.
www.digitalcollegelibrary.com
Chapter -8
Access Specifiers & Data Hiding
8.1 Definition of Access Specifiers
Access specifiers determine how the members (attributes and
methods) of a class can be accessed. They play a critical role in
controlling the visibility of class members and, consequently, in
enforcing encapsulation. C++ provides three access specifiers:
Public: Members are accessible from outside the class.
Private: Members are accessible only within the class itself.
Protected: Members are accessible within the class and by
derived classes.
Using access specifiers effectively helps to maintain a clear and
robust design in software development, allowing for controlled
access to the internal workings of classes.
8.2 Public Access Specifiers
The public access specifier allows class members to be accessible
from outside the class. This means that any code that has visibility
of the class can access its public members.
Syntax and Example
Here’s a simple example illustrating the use of public access
specifiers:
#include <iostream>
using namespace std;
www.digitalcollegelibrary.com
class Rectangle {
public:
// Public attributes
int length;
int width;
// Public method
int area() {
return length * width;
};
int main() {
Rectangle rect;
rect.length = 5; // Accessible
rect.width = 3; // Accessible
cout << "Area: " << rect.area() << endl; // Accessible
return 0;
Characteristics of Public Members
1. Accessibility: Public members can be accessed from
anywhere in the program that has visibility of the class
instance.
www.digitalcollegelibrary.com
2. Intended Use: Public members usually include methods that
are intended to be used by the class users and attributes
that are meant to be shared.
8.3 Private Access Specifiers
The private access specifier restricts access to class members.
Private members can only be accessed from within the class itself,
meaning they cannot be accessed directly from outside the class.
Syntax and Example
#include <iostream>
using namespace std;
class Rectangle {
private:
int length;
int width;
public:
// Constructor
Rectangle(int l, int w) : length(l), width(w) {}
// Public method to access private members
int area() {
return length * width;
www.digitalcollegelibrary.com
}
};
int main() {
Rectangle rect(5, 3);
// rect.length = 5; // Error: 'length' is private
cout << "Area: " << rect.area() << endl; // Accessible through
public method
return 0;
Characteristics of Private Members
1. Encapsulation: Private members are used to implement
encapsulation by hiding the internal state and requiring all
access to be performed through public methods.
2. Data Integrity: This restriction helps in maintaining data
integrity and prevents unauthorized or unintended
modification.
8.4 Protected Access Specifiers
The protected access specifier allows members to be accessible
within the class itself and by derived classes. However, they are
not accessible from outside the class or derived classes.
Syntax and Example
#include <iostream>
using namespace std;
www.digitalcollegelibrary.com
class Shape {
protected:
int width;
int height;
public:
Shape(int w, int h) : width(w), height(h) {}
};
class Rectangle : public Shape {
public:
Rectangle(int w, int h) : Shape(w, h) {}
int area() {
return width * height; // Accessible because width is
protected
};
int main() {
Rectangle rect(5, 3);
cout << "Area: " << rect.area() << endl; // Accessible through
public method
www.digitalcollegelibrary.com
return 0;
Characteristics of Protected Members
1. Visibility: Protected members are accessible in the class itself
and by derived classes, allowing for a controlled way to
extend class functionality.
2. Inheritance: They are useful when you want derived classes
to have access to base class members while keeping them
hidden from the outside world.
8.5 Data Hiding
Data hiding is a fundamental concept that involves restricting
access to the internal state of an object. This is primarily achieved
through the use of private and protected access specifiers.
Importance of Data Hiding
1. Security: By hiding data, you prevent unauthorized access
and modification, which can lead to data corruption or
unexpected behavior.
2. Modularity: Data hiding encourages a modular design,
allowing changes to be made to the implementation of a
class without affecting other parts of the program.
3. Maintenance: It improves maintainability by exposing only
what is necessary and reducing the interdependencies
among different parts of the code.
How to Implement Data Hiding
Use private access specifiers for attributes that should not be
directly accessible from outside the class.
Provide public methods (getters and setters) to control
access to private data, allowing for validation and
transformation.
www.digitalcollegelibrary.com
Example of Data Hiding
#include <iostream>
using namespace std;
class BankAccount {
private:
double balance;
public:
BankAccount(double initialBalance) : balance(initialBalance) {}
// Getter for balance
double getBalance() {
return balance;
// Setter for balance with validation
void deposit(double amount) {
if (amount > 0) {
balance += amount;
www.digitalcollegelibrary.com
void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
};
int main() {
BankAccount account(1000.0);
account.deposit(500.0);
cout << "Balance: " << account.getBalance() << endl; //
Output: Balance: 1500
account.withdraw(200.0);
cout << "Balance: " << account.getBalance() << endl; //
Output: Balance: 1300
// account.balance = 2000; // Error: 'balance' is private
return 0;
www.digitalcollegelibrary.com
Chapter -9
Friend Function & Friend Class
9.1 Introduction
In C++, access specifiers (public, private, and protected) are used
to control the visibility of class members. However, there are
situations where it is beneficial to allow specific external functions
or classes to access these members directly. This is accomplished
through the use of friend functions and friend classes.
Friend Function: A friend function is a non-member function
that can access the private and protected members of a
class. It is declared within the class definition using the friend
keyword.
Friend Class: A friend class is a class that has access to the
private and protected members of another class. This means
that all member functions of the friend class can access the
private members of the class that declares it as a friend.
These features are particularly useful in cases where certain
operations require access to an object’s internal state, but where
it does not make sense for those operations to be part of the
class’s public interface.
9.2 Friend Function
Definition and Purpose
A friend function is a function that is not a member of a class but is
granted access to the class's private and protected members. This
can be particularly useful when a function needs to operate on
the private data of multiple classes or when it is designed to be a
www.digitalcollegelibrary.com
utility function that does not logically belong to any particular
class.
Syntax and Declaration
To declare a function as a friend within a class, you simply use the
friend keyword before the function declaration. Here’s the syntax:
class ClassName {
friend ReturnType FunctionName(Parameters);
// Class members...
};
Example of Friend Functions
Here is an example demonstrating the use of friend functions in
C++:
#include <iostream>
using namespace std;
class Box {
private:
double length;
public:
Box(double len) : length(len) {}
// Friend function declaration
friend void printLength(Box b);
www.digitalcollegelibrary.com
};
// Friend function definition
void printLength(Box b) {
cout << "Length of box: " << b.length << endl; // Accessing
private member
int main() {
Box box(10.5);
printLength(box); // Function can access private member
return 0;
When to Use Friend Functions
Friend functions are useful in the following scenarios:
1. Overloading Operators: When you need to define operator
overloads for classes that require access to private data of
both classes.
2. Utility Functions: When you need functions that operate on
multiple objects of different classes but do not belong to any
single class.
3. Collaboration Between Classes: When you have classes that
need to work closely together, allowing one class to access
the private members of another can simplify
implementation.
www.digitalcollegelibrary.com
9.3 Friend Classes
Definition and Purpose
A friend class is a class that can access the private and protected
members of another class. This can be useful when one class
needs to collaborate closely with another, allowing for greater
flexibility in the design of classes and their interactions.
Syntax and Declaration
To declare a class as a friend of another class, use the friend
keyword followed by the class declaration. Here’s the syntax:
class ClassName1; // Forward declaration
class ClassName2 {
friend class ClassName1; // ClassName1 is a friend of
ClassName2
// Class members...
};
Example of Friend Classes
Below is an example demonstrating the use of friend classes in
C++:
#include <iostream>
using namespace std;
class Box; // Forward declaration
class BoxManager {
www.digitalcollegelibrary.com
public:
void setLength(Box &b, double len); // Function to set length of
Box
};
class Box {
private:
double length;
public:
Box() : length(0) {}
// Granting access to BoxManager
friend class BoxManager;
};
// Definition of BoxManager's member function
void BoxManager::setLength(Box &b, double len) {
b.length = len; // Accessing private member
int main() {
Box box;
www.digitalcollegelibrary.com
BoxManager manager;
manager.setLength(box, 10.5); // Setting length using
BoxManager
return 0;
When to Use Friend Classes
Friend classes are particularly useful in the following cases:
1. Tight Coupling: When two classes are closely related and
need to work together, allowing one to access the internals
of the other can simplify the interaction.
2. Helper Classes: When you have a class designed to assist
another class, it might be beneficial to give the helper class
access to private members.
3. Factory Classes: In factory design patterns, where a class
creates instances of another class, friend classes can access
private constructors.
www.digitalcollegelibrary.com
Chapter -10
Static Members
10.1 Introduction to Static Members
In C++, static members are class-level entities that are shared
among all instances of the class. Unlike instance variables, which
maintain separate copies for each object, static variables retain a
single value regardless of how many objects of the class are
created. Similarly, static functions can be invoked without needing
an object of the class, and they can only access static variables.
Understanding static members is essential for efficient resource
management and effective design patterns. They can facilitate
shared data among objects and provide utility functions that
enhance code organization.
10.2 Static Variables
Definition and Purpose
A static variable in a class is a variable that is shared among all
instances of the class. It retains its value across all instances and
exists for the lifetime of the program. Static variables are often
used for counting the number of objects created or maintaining a
shared state across all instances.
Syntax and Declaration
To declare a static variable within a class, use the static keyword.
Here’s the syntax:
class ClassName {
www.digitalcollegelibrary.com
public:
static DataType variableName;
// Other members...
};
// Definition outside the class
DataType ClassName::variableName = initialValue; // Static
variable definition
Example of Static Variables
Let's consider an example where we track the number of
instances of a class:
#include <iostream>
using namespace std;
class InstanceCounter {
private:
static int count; // Static variable declaration
public:
InstanceCounter() {
count++; // Increment count when a new instance is created
www.digitalcollegelibrary.com
// Static function to get the count of instances
static int getCount() {
return count; // Accessing static variable
};
// Definition of the static variable
int InstanceCounter::count = 0;
int main() {
InstanceCounter obj1; // count = 1
InstanceCounter obj2; // count = 2
InstanceCounter obj3; // count = 3
cout << "Total instances created: " <<
InstanceCounter::getCount() << endl; // Output: 3
return 0;
Use Cases for Static Variables
Static variables are particularly useful in various scenarios,
including:
1. Counting Instances: As shown in the example, static variables
can track how many instances of a class have been
created.
www.digitalcollegelibrary.com
2. Shared Configuration: When multiple instances need to share
configuration settings or parameters, static variables can
store this information centrally.
3. Global State: In situations where certain data should be
accessible to all instances but should not be exposed
publicly, static variables can maintain that state.
10.3 Static Functions
Definition and Purpose
A static function is a member function that can be called without
creating an instance of the class. This type of function can only
access static variables and other static functions. Static functions
are useful for utility functions that do not depend on instance-
specific data.
Syntax and Declaration
To declare a static function within a class, use the static keyword.
Here’s the syntax:
class ClassName {
public:
static ReturnType functionName(Parameters);
// Other members...
};
Example of Static Functions
Here’s an example demonstrating the use of static functions:
#include <iostream>
using namespace std;
www.digitalcollegelibrary.com
class MathUtility {
public:
// Static function to calculate the square of a number
static int square(int number) {
return number * number;
};
int main() {
int num = 5;
cout << "Square of " << num << " is: " << MathUtility::square(num)
<< endl; // Output: 25
return 0;
Use Cases for Static Functions
Static functions can be particularly beneficial in scenarios such as:
1. Utility Functions: Functions that provide general functionality
and do not depend on the state of an object can be made
static.
2. Factory Methods: Static functions can be used to create
instances of a class, often referred to as factory methods.
3. Static Member Access: Static functions can provide access
to static member variables, making them useful for
encapsulating behavior related to shared data.
www.digitalcollegelibrary.com
Chapter -11
this Pointer
11.1 Introduction to this Pointer
The this pointer is a fundamental concept in C++ that enables
classes to reference their own instances. It is automatically
created by the compiler for every non-static member function of
a class. The pointer points to the object for which the member
function was called, allowing access to the object's members and
methods.
By utilizing the this pointer, programmers can ensure that they are
working with the correct instance of a class, enhancing clarity and
reducing ambiguity in code.
11.2 Definition and Behavior
The this pointer has the following characteristics:
Type: The type of the this pointer is a constant pointer to the
type of the class. For example, within a class MyClass, this is
of type MyClass*.
Scope: The this pointer is only accessible within non-static
member functions. It is not available in static member
functions or global functions.
Const Qualifier: Inside a const member function, the this
pointer is treated as a pointer to a const object, meaning
that you cannot modify the object’s members through it.
Syntax Example
Here’s a brief illustration of how the this pointer is used in a
member function:
www.digitalcollegelibrary.com
class MyClass {
public:
int value;
MyClass(int value) {
this->value = value; // Using this pointer to differentiate
between the member variable and the parameter
};
In this example, this->value explicitly refers to the member variable
value, while value refers to the parameter passed to the
constructor.
11.3 Use Cases of this Pointer
Accessing Object Members
The primary use of the this pointer is to access the members of the
current object. This is particularly useful when there are naming
conflicts between parameters and member variables.
class Rectangle {
private:
int width, height;
public:
Rectangle(int width, int height) {
www.digitalcollegelibrary.com
this->width = width; // Resolve conflict between parameter
and member variable
this->height = height; // Resolve conflict
int area() {
return this->width * this->height; // Accessing members using
this
};
Returning the Calling Object
The this pointer can be used to return the calling object from a
member function. This is particularly useful in method chaining.
class Counter {
private:
int count;
public:
Counter() : count(0) {}
Counter& increment() {
this->count++;
return *this; // Return the current object
www.digitalcollegelibrary.com
int getCount() const {
return this->count;
};
int main() {
Counter c;
c.increment().increment(); // Method chaining
std::cout << c.getCount(); // Output: 2
return 0;
Enabling Method Chaining
Method chaining is a powerful feature in object-oriented
programming that allows multiple member function calls to be
linked in a single statement. This is achieved by returning *this from
member functions, as demonstrated in the previous example.
Distinguishing Between Parameters and Members
In member functions, the this pointer is crucial for distinguishing
between member variables and function parameters that share
the same name. Using this helps to clarify the intent and improve
code readability.
class Employee {
private:
std::string name;
www.digitalcollegelibrary.com
public:
Employee(std::string name) {
this->name = name; // Clearly distinguish member variable
from parameter
};
www.digitalcollegelibrary.com
Chapter -12
Type Casting
12.1 Introduction to Type Casting
Type casting in C++ refers to the conversion of one data type to
another. This is particularly significant in object-oriented
programming, where objects can belong to different classes,
especially when dealing with class hierarchies. There are four
primary types of casting provided by C++: static_cast,
dynamic_cast, const_cast, and reinterpret_cast. Each of these
casting operators serves a specific purpose and can affect the
behavior of a program in different ways.
Static Casting: Used for conversions that are well-defined at
compile time.
Dynamic Casting: Primarily used for safely downcasting in
inheritance hierarchies.
Const Casting: Used to add or remove the const qualifier
from a variable.
Reinterpret Casting: Used for low-level casts that reinterpret
the bits of a variable.
Understanding these casting types is essential for writing robust
C++ code, especially in applications involving complex class
hierarchies and polymorphism.
12.2 Static Cast (static_cast)
Syntax and Use Cases
The static_cast operator is the most common type of cast in C++.
It is used for conversions between compatible types, including:
www.digitalcollegelibrary.com
Converting between numeric types (e.g., int to float).
Converting pointers within an inheritance hierarchy
(upcasting and downcasting when you are sure of the type).
Converting between related types.
The syntax for static_cast is as follows:
T target = static_cast<T>(expression);
Where T is the target type, and expression is the value to be
converted.
Example
Here’s an example demonstrating the use of static_cast for
converting numeric types and performing upcasting and
downcasting in a class hierarchy:
#include <iostream>
using namespace std;
class Base {
public:
virtual void show() {
cout << "Base Class" << endl;
};
class Derived : public Base {
public:
void show() override {
www.digitalcollegelibrary.com
cout << "Derived Class" << endl;
};
int main() {
// Numeric conversion
double d = 3.14;
int i = static_cast<int>(d); // double to int
cout << "Converted double to int: " << i << endl;
// Upcasting
Derived* derived = new Derived();
Base* base = static_cast<Base*>(derived); // Upcasting
base->show(); // Output: Derived Class
// Downcasting
Derived* derivedAgain = static_cast<Derived*>(base); //
Downcasting
derivedAgain->show(); // Output: Derived Class
delete derived;
return 0;
www.digitalcollegelibrary.com
Output:
Converted double to int: 3
Derived Class
Derived Class
In this example, we first convert a double to an int using
static_cast. We also demonstrate upcasting from Derived to Base
and downcasting back to Derived using static_cast.
12.3 Dynamic Cast (dyanmic_cast)
Syntax and Use Cases
The dynamic_cast operator is primarily used for safe downcasting
in inheritance hierarchies. It allows for checking the type of an
object at runtime and is used with polymorphic types (classes with
virtual functions). If the cast is not valid, dynamic_cast will return
nullptr for pointer types or throw an exception for references.
The syntax for dynamic_cast is:
T* target = dynamic_cast<T*>(expression);
Where T is the target type (usually a derived class), and expression
is a pointer to the base class.
Example
Here’s an example demonstrating the use of dynamic_cast for
safe downcasting:
#include <iostream>
using namespace std;
class Base {
www.digitalcollegelibrary.com
public:
virtual void show() {
cout << "Base Class" << endl;
virtual ~Base() {} // Virtual destructor for proper cleanup
};
class Derived : public Base {
public:
void show() override {
cout << "Derived Class" << endl;
void derivedMethod() {
cout << "Method specific to Derived Class" << endl;
};
class AnotherDerived : public Base {
public:
void show() override {
cout << "Another Derived Class" << endl;
www.digitalcollegelibrary.com
};
int main() {
Base* basePtr = new Derived(); // Upcasting
basePtr->show(); // Output: Derived Class
// Dynamic downcasting
Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);
if (derivedPtr) {
derivedPtr->derivedMethod(); // Output: Method specific to
Derived Class
} else {
cout << "Downcast failed!" << endl;
// Attempting to downcast to a different derived class
AnotherDerived* anotherDerivedPtr =
dynamic_cast<AnotherDerived*>(basePtr);
if (anotherDerivedPtr) {
anotherDerivedPtr->show();
} else {
cout << "Downcast to AnotherDerived failed!" << endl; // This
will be executed
www.digitalcollegelibrary.com
delete basePtr;
return 0;
Output:
Derived Class
Method specific to Derived Class
Downcast to AnotherDerived failed!
In this example, dynamic_cast is used to safely downcast basePtr
to Derived. The downcast is checked, and if it fails, a message is
printed instead of causing undefined behavior.
12.4 Const Cast (const_cast)
Syntax and Use Cases
The const_cast operator is used to add or remove the const
qualifier from a variable. It can be helpful when you need to
modify an object that was originally declared as const, but you
are sure that it can be modified.
The syntax for const_cast is:
T* target = const_cast<T*>(expression);
Where T is the type to which you want to cast and expression is
the original variable.
Example
Here’s an example that demonstrates const_cast:
#include <iostream>
www.digitalcollegelibrary.com
using namespace std;
void modifyValue(const int* p) {
// Attempting to modify the value pointed to by p
int* modifiable = const_cast<int*>(p);
*modifiable = 20; // Modifying the value (undefined behavior if
original was const)
int main() {
int x = 10;
const int* ptr = &x;
cout << "Before: " << *ptr << endl; // Output: 10
modifyValue(ptr);
cout << "After: " << *ptr << endl; // Output: 20 (if modification is
allowed)
return 0;
Output:
Before: 10
After: 20
www.digitalcollegelibrary.com
In this example, const_cast is used to remove the const qualifier
from the pointer ptr, allowing the value to be modified. However,
caution should be exercised, as modifying a truly const object
leads to undefined behavior.
12.5 Reinterpret Cast (reinterpret_cast)
Syntax and Use Cases
The reinterpret_cast operator is used for low-level casts that
reinterpret the bits of a variable. This cast does not check type
safety, which means that it should be used with caution. It can
convert any pointer type to any other pointer type and is often
used for interfacing with hardware or other low-level programming
tasks.
The syntax for reinterpret_cast is:
T* target = reinterpret_cast<T*>(expression);
Example
Here’s an example demonstrating the use of reinterpret_cast:
#include <iostream>
using namespace std;
struct Data {
int a;
float b;
};
int main() {
www.digitalcollegelibrary.com
Data data = { 10, 3.14f };
void* ptr = reinterpret_cast<void*>(&data); // Convert to void
pointer
// Reinterpreting the void pointer back to Data*
Data* dataPtr = reinterpret_cast<Data*>(ptr);
cout << "Data: a = " << dataPtr->a << ", b = " << dataPtr->b <<
endl;
// Casting between incompatible pointer types
int* intPtr = reinterpret_cast<int*>(&data);
cout << "Data as int: " << *intPtr << endl; // Output: 10 (not a
valid interpretation of the structure)
return 0;
Output:
Data: a = 10, b = 3.14
Data as int: 10
In this example, reinterpret_cast is used to convert a pointer to a
structure into a void* and back. Additionally, it demonstrates
casting to an incompatible pointer type, showcasing the low-level
nature of reinterpret_cast.
www.digitalcollegelibrary.com