Introduction to OOP in C++
When we start learning programming, we usually begin with variables, conditions, loops, functions, and arrays.
For small programs, this approach works very well.
But imagine that you are building a large application such as a banking system, an e-commerce platform, a game, or a college management system.
Suddenly, you may have thousands of variables, hundreds of functions, and many different parts of the application interacting with each other.
At that point, simply writing more functions is not enough.
We need a better way to organize the code.
This is where Object-Oriented Programming (OOP) comes in.
OOP is one of the most important programming concepts to understand, especially if you are learning C++.
In this article, we will understand OOP from the ground up.
We will start with the basic idea of objects and classes and then understand the four major concepts of OOP:
- Encapsulation
- Abstraction
- Inheritance
- Polymorphism
We will also understand constructors, destructors, and how all these concepts come together when building real applications.
What is Object-Oriented Programming?
OOP stands for Object-Oriented Programming.
The easiest way to understand OOP is to first look at the real world.
Think about a Car.
A car has certain properties.
For example, it has:
- Brand
- Model
- Color
- Speed
- Fuel level
At the same time, a car can perform certain actions.
It can:
- Start
- Stop
- Accelerate
- Brake
- Turn
So we can think of a car as a combination of data and behavior.
The same idea can be applied to programming.
Instead of keeping data in one place and functions somewhere else, OOP allows us to group related data and behavior together inside an object.
For example, a Car object could contain information about its speed and color, along with functions that allow it to accelerate or stop.
This makes the program easier to understand because the code representing a particular thing is kept together.
So, in simple words:
Object-Oriented Programming is a programming approach where we organize software around objects that contain data and behavior.
Why Do We Need OOP?
To understand why OOP is useful, let's imagine that we are building a banking application.
Suppose we have 10 customers.
Each customer has:
- Name
- Account number
- Balance
And each customer can:
- Deposit money
- Withdraw money
- Check balance
Without a proper structure, we might end up creating many variables and functions separately.
As the application grows from 10 customers to 10,000 customers, managing all these variables and functions becomes increasingly difficult.
There is another problem.
What if any part of the program can directly modify a customer's balance?
For example, imagine that some unrelated part of the program accidentally changes:
balance = 50000
to:
balance = -100000
We need a way to control which parts of our program are allowed to access and modify certain data.
OOP helps us solve these kinds of problems by giving our code a clear structure.
It helps us:
- Organize large applications
- Keep related data and behavior together
- Reuse existing code
- Protect important data
- Make applications easier to maintain
- Model real-world entities naturally
The important thing to understand is that OOP is not simply a way of writing more code.
The real purpose of OOP is to make complex software easier to design and manage.
Understanding Classes and Objects
Before learning the four pillars of OOP, we need to understand two fundamental concepts:
Class and Object.
These two terms appear everywhere in object-oriented programming.
What is a Class?
A class can be thought of as a blueprint or template.
Let's take the example of a house.
Before constructing houses, an architect usually creates a blueprint.
The blueprint describes:
- Number of rooms
- Position of doors
- Position of windows
- Structure of the house
But the blueprint itself is not a real house.
It is simply a design that tells us how the house should be constructed.
A class works in a similar way.
A class defines what an object should contain and what it should be able to do.
For example:
class Car {
// Data
// Functions
};
Here, Car is a class.
The class can describe things such as the car's color, speed, and model, along with behaviors such as starting and stopping.
The important point is:
A class is a blueprint. It describes the structure and behavior of objects.
What is an Object?
An object is the actual instance of a class.
If a class is the blueprint of a house, then the actual houses built using that blueprint are objects.
For example, suppose we have:
class Car {
};
We can create objects from it:
Car car1;
Car car2;
Car car3;
Here:
- Car is the class.
- car1 is an object.
- car2 is an object.
- car3 is an object.
All three objects follow the structure defined by the Car class.
However, each object can contain different data.
For example:
car1 → Toyota
car2 → BMW
car3 → Tesla
The class defines the common structure, while each object represents an individual instance.
This gives us a very important relationship:
Class = Blueprint
Object = Actual instance created from the blueprint
Data and Behavior
One of the most important ideas in OOP is that an object can contain both data and behavior.
Let's take a Student.
A student has certain information:
- Name
- Age
- Roll number
- Marks
This is the student's data.
A student can also perform certain actions:
- Study
- Attend class
- Take an exam
- Submit an assignment
These are the student's behaviors.
In C++, data is usually represented using variables, while behavior is represented using functions.
So conceptually:
Object
|
|--- Data
|
|--- Behavior
This is one of the biggest differences between thinking about programming using only separate functions and thinking in terms of objects.
Instead of asking:
"Where should I put this variable?"
we can think:
"Which object should own this data?"
And instead of asking:
"Which function should handle this?"
we can think:
"Which object should be responsible for this behavior?"
This way of thinking becomes extremely useful when applications become large.
The Four Pillars of OOP
Once we understand classes and objects, we can move to the four major concepts of Object-Oriented Programming.
These are commonly called the four pillars of OOP:
- Encapsulation
- Abstraction
- Inheritance
- Polymorphism
Each pillar solves a different problem.
Let's understand them carefully.
1. Encapsulation
What is Encapsulation?
Let's start with a real-world example.
Consider your bank account.
You can interact with your account in certain ways.
You can:
- Deposit money
- Withdraw money
- Check your balance
But you cannot directly access the bank's internal database and change your balance whenever you want.
The bank controls how the balance is modified.
This is the basic idea behind encapsulation.
Encapsulation means combining data and the functions that operate on that data together while controlling access to that data.
In C++, we can control access using access specifiers.
The three important access specifiers are:
- public
- private
- protected
Public
Anything declared as public can generally be accessed from outside the class through an object.
For example:
class Student {
public:
string name;
};
We can access the name from outside the class.
Student student;
student.name = "Prakash";
The data is publicly accessible.
Private
Anything declared as private can only be accessed from within the class itself.
For example:
class BankAccount {
private:
double balance;
};
Code outside the class cannot directly modify balance.
This is useful when we want the class to control how its data is changed.
For example, instead of allowing someone to directly change the balance, we can provide a function such as:
deposit()
The function can check whether the amount is valid before changing the balance.
This gives us much better control over the data.
Protected
The protected access specifier is mainly useful when inheritance is involved.
Protected members can be accessed inside:
- The class itself
- Derived classes
But they cannot normally be accessed directly from outside through an object.
We will understand why this becomes useful when we discuss inheritance.
Why is Encapsulation Important?
Imagine a large application with thousands of variables.
If every part of the program can modify every variable directly, maintaining the application becomes extremely difficult.
Encapsulation creates boundaries.
It allows a class to decide:
"This data is internal and should be protected."
and:
"These are the operations that other parts of the program are allowed to perform."
This makes code safer and easier to maintain.
A simple way to remember encapsulation is:
Encapsulation = Protect the data and control access to it.
2. Abstraction
Now let's move to another important concept: abstraction.
Abstraction is something we use in everyday life without even realizing it.
Think about driving a car.
You know how to:
- Turn the steering wheel
- Press the accelerator
- Press the brake
- Change gears
But you don't need to understand every internal detail of the engine.
You don't need to know exactly how fuel is injected, how the engine converts fuel into motion, or how every component communicates internally.
You simply use the controls provided to you.
This is abstraction.
Abstraction means hiding unnecessary implementation details and exposing only the important parts to the user.
A Simple Programming Example
Imagine that your program has a function:
car.start();
When you call this function, you don't need to know everything happening inside it.
The function may internally perform many operations.
But from the outside, you only need to know:
start() → Starts the car
The complexity is hidden behind a simple interface.
This is exactly what abstraction tries to achieve.
Why is Abstraction Useful?
Imagine using a banking application.
You press:
Transfer Money
You don't need to understand:
- Database transactions
- Network communication
- Authentication
- Encryption
- Server-side validation
All of that complexity is hidden behind a simple interface.
You only interact with the part that matters to you.
This allows complex systems to remain usable.
Without abstraction, users would need to understand every internal detail of the system before they could use it.
Encapsulation vs Abstraction
These two concepts are often confused because they are closely related.
A simple way to distinguish them is:
Encapsulation
Focuses on protecting and controlling access to data.
Think:
"Who is allowed to access this?"
Abstraction
Focuses on hiding unnecessary implementation details.
Think:
"What details does the user actually need to know?"
For example, in a bank account:
Encapsulation can protect the balance variable from direct modification.
Abstraction allows the user to simply call:
withdraw()
without knowing how the withdrawal is processed internally.
So:
Encapsulation protects the implementation.
Abstraction hides the complexity.
3. Inheritance
Now let's talk about inheritance.
Inheritance is mainly about reusing existing functionality and creating relationships between classes.
Imagine we have a general class called:
Animal
Animals generally have some common characteristics.
They can:
- Eat
- Sleep
- Move
Now we want to create classes for:
- Dog
- Cat
- Horse
All of these animals share some common behavior.
Instead of writing the same behavior separately in every class, we can create a common Animal class and allow other classes to inherit from it.
This is called inheritance.
Inheritance allows a new class to acquire properties and behaviors from an existing class.
Base Class and Derived Class
The existing class is called the base class or parent class.
The class that inherits from it is called the derived class or child class.
Conceptually:
Animal
|
----------------
| |
Dog Cat
Here:
- Animal is the base class.
- Dog is a derived class.
- Cat is a derived class.
The derived classes can use functionality provided by the base class.
They can also add their own functionality.
For example:
Animal
|
|--- eat()
|--- sleep()
|
+--- Dog
| |
| +--- bark()
|
+--- Cat
|
+--- meow()
The common behavior is placed in the base class.
Specialized behavior can be added to the derived classes.
Real-World Example of Inheritance
Consider a company.
We can have a general concept called:
Employee
An employee may have:
- Name
- Employee ID
- Salary
Now the company may have different types of employees:
Employee
|
|--- Developer
|
|--- Designer
|
|--- Manager
All of them are employees.
Therefore, they can share common employee information and behavior.
But each role can also have its own specialized behavior.
A developer may write code.
A designer may create designs.
A manager may manage a team.
This is a good example of inheritance because there is a clear "is-a" relationship.
Developer is an Employee.
Manager is an Employee.
Designer is an Employee.
When Should We Use Inheritance?
Inheritance should not simply be used because it allows us to reuse code.
There should be a meaningful relationship between the classes.
A useful question to ask is:
"Is the derived class actually a type of the base class?"
For example:
Dog is an Animal
Car is a Vehicle
Manager is an Employee
These relationships make sense.
But something like:
Car is an Engine
does not make sense.
A car has an engine, rather than being an engine.
This distinction becomes important when designing real-world applications.
4. Polymorphism
Now we reach one of the most powerful concepts in OOP:
Polymorphism.
The word comes from two parts:
Poly → Many
Morph → Forms
So polymorphism essentially means:
One interface, many forms of behavior.
Let's understand this with a simple example.
Same Action, Different Behavior
Consider animals again.
A dog and a cat can both make a sound.
We can think of the common action as:
makeSound()
But the actual behavior is different.
Dog → Woof
Cat → Meow
The operation is conceptually the same.
But the implementation is different.
This is polymorphism.
The program can work with the common concept of an animal while allowing each specific animal to behave differently.
Another Example: Payment Systems
Imagine an online shopping application.
The application needs to make a payment.
It could support:
- UPI
- Credit Card
- Debit Card
- Net Banking
- PayPal
From the application's perspective, the common operation is:
makePayment()
But the actual process is different for every payment method.
For example:
UPI
↓
makePayment()
Credit Card
↓
makePayment()
PayPal
↓
makePayment()
The application can interact with all of them through a common interface while each implementation performs the payment differently.
This is where polymorphism becomes extremely useful.
Types of Polymorphism in C++
Polymorphism in C++ is commonly divided into two major categories:
- Compile-time polymorphism
- Runtime polymorphism
Let's understand both.
Compile-Time Polymorphism
In compile-time polymorphism, the compiler decides which function or operation should be used during compilation.
Two common examples are:
- Function overloading
- Operator overloading
Function Overloading
Function overloading means having multiple functions with the same name but different parameters.
For example:
int add(int a, int b);
double add(double a, double b);
Both functions are called add.
But they accept different types of arguments.
When the program is compiled, the compiler determines which version should be called.
For example:
add(10, 20)
uses the integer version.
While:
add(10.5, 20.5)
uses the double version.
The function name remains the same, but its behavior is selected based on the arguments.
Runtime Polymorphism
Runtime polymorphism is different.
Here, the behavior is determined while the program is running.
In C++, runtime polymorphism is commonly achieved through:
- Inheritance
- Function overriding
- Virtual functions
Imagine we have a base class:
Shape
and derived classes:
Circle
Rectangle
Triangle
Each shape can have an:
area()
function.
But the way area is calculated is different for every shape.
Circle → π × r²
Rectangle → width × height
Triangle → ½ × base × height
The interface can remain the same:
area()
while the implementation changes depending on the actual object.
That is runtime polymorphism.
Constructors
Now that we understand classes and objects, let's look at another important C++ concept: constructors.
When we create an object, we often need to initialize it with some initial data.
For example, suppose we create a Student object.
We may want to provide:
- Name
- Age
- Roll number
A constructor helps us initialize an object when it is created.
A constructor is a special function that is automatically called when an object is created.
For example:
class Student {
public:
Student() {
// initialization
}
};
Notice that the constructor has the same name as the class.
The important thing to remember is:
Constructor → Automatically runs when an object is created.
Constructors are commonly used to initialize the initial state of objects.
Types of Constructors
C++ supports different forms of constructors.
The most common ones you will encounter are:
Default Constructor
A constructor that does not require arguments.
Student();
It can be used when an object is created without providing additional information.
Parameterized Constructor
A constructor that accepts values.
For example:
Student(string name, int age);
This allows us to create an object with initial values.
Copy Constructor
A copy constructor is used to create a new object based on an existing object.
Conceptually:
Existing Object
↓
Copy Constructor
↓
New Object
Copy constructors become particularly important when learning how C++ manages objects, copying, and resources.
Destructors
If a constructor is associated with creating an object, a destructor is associated with destroying an object.
A destructor is automatically called when an object is destroyed.
A destructor has the same name as the class but starts with a ~.
For example:
~Student();
The basic lifecycle looks like this:
Object Created
↓
Constructor Called
↓
Object Used
↓
Object Destroyed
↓
Destructor Called
Destructors are particularly important when a class manages resources such as:
- Dynamically allocated memory
- Files
- Network resources
- Other system resources
The destructor provides a place to perform cleanup when the object is no longer needed.
Access Specifiers in C++
We briefly mentioned access specifiers while discussing encapsulation.
Let's understand them more clearly.
C++ provides three major access specifiers:
| Access Specifier | Meaning |
|---|---|
| public | Accessible from outside the class |
| private | Accessible only inside the class |
| protected | Accessible inside the class and derived classes |
The default access level for members of a class in C++ is private.
For example:
class Student {
string name;
public:
int age;
};
Here, name is private by default, while age is explicitly public.
Access specifiers are one of the mechanisms C++ provides to implement encapsulation.
How OOP Concepts Work Together
One important thing to understand is that the four pillars of OOP are not isolated concepts.
In real applications, they usually work together.
Let's imagine that we are building an e-commerce application.
We may have classes such as:
User
Product
Cart
Order
Payment
Each class represents an important concept in the application.
A Product might contain information such as:
- Name
- Price
- Stock
- Category
A Cart might contain:
- Products
- Quantity
- Total price
A Payment system might support:
- UPI
- Card
- Net Banking
Now let's see how OOP helps us design this system.
Encapsulation in the E-Commerce Application
Some data should not be freely modified.
For example, product stock should not be changed randomly by every part of the application.
Instead, the Product class can control how stock is updated.
This protects the internal state of the object.
Abstraction in the E-Commerce Application
When a customer clicks:
Pay Now
they don't need to understand the internal payment process.
The application hides the complexity behind a simple interface.
That's abstraction.
Inheritance in the E-Commerce Application
Suppose our application has different types of users.
We might have:
User
|
|--- Customer
|
|--- Admin
|
|--- Seller
Common functionality can be placed inside the User class, while specialized behavior can be added to the derived classes.
That's inheritance.
Polymorphism in the E-Commerce Application
Now consider payment methods.
We might have:
Payment
|
|--- UPI
|
|--- CreditCard
|
|--- NetBanking
Each payment type can implement the payment operation differently.
The application can still interact with them through a common payment interface.
That's polymorphism.
OOP Is More Than Just Four Definitions
When people first learn OOP, they often try to memorize:
Encapsulation = ...
Abstraction = ...
Inheritance = ...
Polymorphism = ...
But memorizing definitions is not enough.
The real purpose of OOP is to change the way you think about designing software.
Instead of looking at a large application as thousands of separate lines of code, you start identifying the important entities and responsibilities inside the system.
For example, in a food delivery application, you might identify:
User
Restaurant
Food
Cart
Order
DeliveryPartner
Payment
Each of these can have its own data and behavior.
Then you decide:
- Which data should be private?
- Which operations should be exposed?
- Which classes have relationships?
- Which classes can reuse functionality?
- Where is polymorphism useful?
This is where OOP becomes a design tool rather than simply a C++ feature.
Composition vs Inheritance
There is another important idea that becomes useful as you become better at OOP.
Not every relationship should be implemented using inheritance.
Sometimes one object simply contains another object.
For example:
Car
|
|--- Engine
|--- Wheels
|--- Battery
A car is not an engine.
A car has an engine.
This is called a has-a relationship.
Inheritance usually represents an is-a relationship.
For example:
Dog is an Animal
Car is a Vehicle
Manager is an Employee
Composition represents a has-a relationship.
For example:
Car has an Engine
Computer has a Processor
House has a Room
This distinction becomes very important when designing larger systems.
A good OOP design is not about using inheritance everywhere.
It is about choosing the relationship that actually represents the problem.
Where Is OOP Used?
OOP is used across many areas of software development.
You will find object-oriented concepts in:
- Banking systems
- E-commerce applications
- Game development
- Desktop applications
- Enterprise software
- Backend systems
- GUI frameworks
- Operating system components
- Large-scale business applications
C++ itself is heavily associated with OOP, but the concepts are not limited to C++.
Languages such as Java, C#, Python, and many others also support object-oriented programming.
The syntax changes between languages, but the underlying ideas remain similar.
Is OOP Always Necessary?
OOP is powerful, but it does not mean every program needs classes.
Suppose you want to write a tiny program that adds two numbers.
Creating several classes for such a simple problem would make the program unnecessarily complicated.
OOP becomes more useful when:
- The application is becoming large.
- Many entities interact with each other.
- Data needs controlled access.
- Code needs to be reused.
- Different parts of the application have different responsibilities.
- The software is expected to evolve over time.
The goal is not:
"Use OOP everywhere."
The goal is:
"Use the right design for the problem."
Good programmers don't use a concept simply because they know it.
They use it when it solves a real problem.
A Simple Mental Model for OOP
If you are just starting with OOP, don't try to memorize everything at once.
Start with this mental model.
First, identify the objects in your problem.
For a banking application:
Customer
Account
Transaction
Bank
Then think about what data each object owns.
For an account:
Account
|
|--- Account Number
|--- Balance
|--- Account Holder
Then think about what that object can do:
Account
|
|--- Deposit
|--- Withdraw
|--- Check Balance
Then ask:
Which data should be protected?
That's encapsulation.
Ask:
Which internal details don't need to be exposed?
That's abstraction.
Ask:
Which classes share common characteristics?
That's where inheritance may be useful.
Ask:
Can the same operation have different implementations?
That's where polymorphism may be useful.
This is a much better way to learn OOP than simply memorizing definitions.
OOP in One Example
Let's bring everything together with a simple example.
Imagine a vehicle management system.
We could have a base concept called:
Vehicle
A vehicle may have:
- Brand
- Speed
- Fuel
And it may perform actions such as:
- Start
- Stop
- Accelerate
Now we can have:
Vehicle
|
|--- Car
|
|--- Bike
|
|--- Truck
This represents inheritance.
The internal data such as fuel or engine state can be protected.
This represents encapsulation.
The user can simply call:
start()
without knowing how the engine internally starts.
This represents abstraction.
And if each vehicle implements:
move()
differently, then we can use polymorphism.
Now all four concepts are working together inside one system.
This is the real purpose of OOP.
The Four Pillars in Simple Words
At this point, we can summarize the four pillars very simply.
Encapsulation
Protect the data.
Keep related data and behavior together and control how the data can be accessed.
Abstraction
Hide the complexity.
Expose only what the user needs and hide unnecessary implementation details.
Inheritance
Reuse and extend.
Create new classes based on existing classes when there is a meaningful relationship.
Polymorphism
Same interface, different behavior.
Allow different objects to respond differently to the same operation.
A simple way to remember them is:
Encapsulation → Protect
Abstraction → Hide
Inheritance → Reuse
Polymorphism → Different behavior
What Should You Learn After OOP?
Once you understand the basic OOP concepts, there are several C++ topics that become much easier to approach.
You can continue with:
- Constructors and destructors
- Constructor overloading
- Copy constructors
- this pointer
- Static members
- Inheritance types
- Function overriding
- Virtual functions
- Pure virtual functions
- Abstract classes
- Multiple inheritance
- Operator overloading
- Friend functions
- Composition
- Association
- Aggregation
- Smart pointers
- Rule of Three
- Rule of Five
You do not need to learn all of these at once.
The important thing is to first build a strong understanding of classes, objects, encapsulation, abstraction, inheritance, and polymorphism.
Once the foundation is clear, the advanced concepts become much easier to understand.
Conclusion
Object-Oriented Programming is not just about creating classes and objects.
It is a way of thinking about software.
When applications become large, we need a way to organize data, behavior, responsibilities, and relationships between different parts of the system.
OOP gives us a structured way to do that.
We start with classes and objects.
Then we use the four pillars to design better systems:
Encapsulation helps us protect data.
Abstraction helps us hide unnecessary complexity.
Inheritance helps us reuse and extend existing functionality.
Polymorphism allows different objects to behave differently through a common interface.
The most important thing is not to memorize these definitions.
Try building something.
Create a simple BankAccount.
Then create a Student management system.
Then try building a small e-commerce or library management application.
As you build, start asking yourself:
What are my objects?
What data should each object own?
What should each object be responsible for?
What should be hidden?
What can be reused?
Where can different objects behave differently?
Once you start thinking this way, OOP stops being a collection of definitions and becomes a practical way of designing software.
