Skip to main content

Object oriented programming

[C++] OOPs - Introduction to Object Oriented Programming

Let's Talk About Object-Oriented Programming (OOP) in C++
Hey there!
So you've just stepped into the world of C++, and suddenly people are throwing around big words like Object-Oriented Programming, encapsulation, polymorphism... and you’re like, “Wait, what did I sign up for?”
Don’t worry — we’ve all been there. And today, we're going to figure it out together. Just two friends having a chat, figuring out how C++ really works behind the scenes.
Let’s start at the very beginning.

Opening the Door: Why Did We Even Need OOP?

Alright, imagine this — we both work at a school. One day, the principal walks in and says:

"Hey, can you build a system that keeps track of all our students, teachers, classes, marks, and attendance?"

We’re excited. We open up C++, and because we’ve learned procedural programming first, we start writing functions and variables like this:

string student1_name = "Alice";
int student1_age = 14;
float student1_marks = 89.5;
 
string student2_name = "Bob";
int student2_age = 15;
float student2_marks = 76.0;
 
void printStudent1() {
    cout << student1_name << ", " << student1_age << ", " << student1_marks << endl;
}

So far, so good. We create another set for the teacher, then one for classes. But then… the system starts growing. More students. More teachers. Multiple functions. Tons of global variables.

And soon — boom! — everything’s messy.

“Wait… which function updates Alice’s marks again? And why does changing one value mess up another?”

Our kitchen has too many cooks, our code has too many moving parts, and suddenly we’re debugging more than we’re building.

That, my friend, is where procedural programming starts to break down.


The Cracks in Procedural Programming

Let’s take a second to see what exactly goes wrong when we rely only on procedures and global variables.

1. Scattered Code, No Structure
We’ve got student data here, teacher data there, and functions all over the place. There’s no connection between what belongs to whom.

2. Repetition Galore
Adding a new student? Copy-paste the same three variables again. And a print function too? That’s six lines minimum for every new entry. It’s exhausting.

3. Hard to Maintain
One small change in how we store student data means updating every function that uses that data. Miss one? Bugs galore.

4. No Real-World Mapping
In real life, a student is a single thing — a person with properties and behaviour. But in procedural programming, we split that across multiple unrelated parts. That’s not intuitive.

So here comes the golden question:

“Isn’t there a way to group everything about a student into one tidy package?”

OOP to the Rescue: Thinking Like the Real World

That’s the big idea behind Object-Oriented Programming (OOP).

OOP says:

“Let’s model our code the way we think about real things — with objects.”

An object is just like a real-world entity. A student. A book. A car.

Each object has:

  • Properties (data) — like name, age, or color
  • Behavior (functions) — like walk, talk, or accelerate

And how do we create these objects in code?
We use a class — a blueprint that defines what an object will look like and how it will behave.


Let’s Define a Student — the OOP Way

Here’s our first simple class in C++:

#include <iostream>
using namespace std;
 
class Student {
public:
    string name;
    int age;
    float marks;
 
    void introduce() {
        cout << "Hi! I'm " << name << ". I'm " << age << " years old, and I scored " << marks << " marks.\n";
    }
};

Now, let’s create two students:

int main() {
    Student s1;
    s1.name = "Alice";
    s1.age = 14;
    s1.marks = 89.5;
 
    Student s2;
    s2.name = "Bob";
    s2.age = 15;
    s2.marks = 76.0;
 
    s1.introduce();
    s2.introduce();
}

Boom! No repetition. Just one class, and multiple objects.

And the best part? Each student carries their own data and functions with them. No more chaos!


But Wait, What Is a Class and Object Exactly?

Let’s take a moment and define things clearly.

Class:
A class is like a template or blueprint. It defines what data and behaviour an object should have.

Think of it like a cookie cutter.

Object:
An object is an actual instance of the class — a real cookie made from the cutter.

So if Student is our class, then s1, s2, s3 are all objects made from that template.


Let’s Pack More into Our Class
Our students have names, ages, and marks. But that’s not all!
Let’s add:

  • A roll number (unique ID)
  • A method to check pass/fail
  • A constructor to initialize easily
💡
For the time being, consider "constructor" as a black box, that creates an object when called !!
class Student {
public:
    string name;
    int age;
    int roll;
    float marks;
 
    Student(string n, int a, int r, float m) {
        name = n;
        age = a;
        roll = r;
        marks = m;
    }
 
    void introduce() {
        cout << "Name: " << name << ", Roll: " << roll << ", Age: " << age << ", Marks: " << marks << endl;
    }
 
    bool hasPassed() {
        return marks >= 40.0;
    }
};

Now using this in main:

int main() {
    Student s1("Alice", 14, 101, 89.5);
    Student s2("Bob", 15, 102, 33.0);
 
    s1.introduce();
    cout << (s1.hasPassed() ? "Passed\n" : "Failed\n");
 
    s2.introduce();
    cout << (s2.hasPassed() ? "Passed\n" : "Failed\n");
}

Much more realistic, right?


Now Let’s Talk OOP vs Procedural: A Comparison

Let’s make a side-by-side breakdown:

Feature

Procedural

OOP

Code Organization

Scattered

Structured via classes

Reusability

Difficult

High (inheritance, objects)

Real-world mapping

Poor

Excellent

Maintenance

Tedious

Easy

Data Safety

Low

High (encapsulation)


Encapsulation — The First Pillar

“Keep data safe, and give access only where it makes sense.”

What if we don’t want anyone to directly modify a student’s marks?

Let’s hide the data using private access, and create controlled functions.

class Student {
private:
    float marks;
 
public:
    void setMarks(float m) {
        if (m >= 0 && m <= 100)
            marks = m;
    }
 
    float getMarks() {
        return marks;
    }
};

Now, we’ve wrapped the data in a safety bubble. This is encapsulation in action.


Abstraction — Show Only What Matters

Abstraction is about hiding complex details. Let the user see only the final result.

For example, this ATM function:

class ATM {
public:
    void withdrawCash() {
        verifyPin();
        checkBalance();
        dispenseCash();
    }
 
private:
    void verifyPin() { /* logic */ }
    void checkBalance() { /* logic */ }
    void dispenseCash() { cout << "Cash withdrawn\n"; }
};

The user just calls withdrawCash() — no idea how it works behind the scenes. And that’s the beauty of abstraction.


Inheritance — Sharing Is Caring

“A baby inherits traits from parents.”

A child class can inherit features from a parent class.

class Animal {
public:
    void eat() {
        cout << "This animal eats food." << endl;
    }
};
 
class Dog : public Animal {
public:
    void bark() {
        cout << "Woof! Woof!" << endl;
    }
};
 
int main() {
    Dog d;
    d.eat();  // Inherited from Animal
    d.bark(); // From Dog
}

The Dog class didn't write the eat() function — it just inherited it. That’s code reuse!


Polymorphism — One Interface, Many Forms

Let’s say both students and teachers have a method role() — but each says something different.
class Person {
public:
    virtual void role() {
        cout << "I play a role in the school." << endl;
    }
};
 
class Student : public Person {
public:
    void role() override {
        cout << "I am a student. I learn and take exams." << endl;
    }
};
 
class Teacher : public Person {
public:
    void role() override {
        cout << "I am a teacher. I teach and guide students." << endl;
    }
};
 
int main() {
    Person* p;
 
    Student s;
    Teacher t;
 
    p = &s;
    p->role();  // Student's version
 
    p = &t;
    p->role();  // Teacher's version
}

That’s runtime polymorphism — one name, many forms.


Why OOP Feels Natural

At the end of the day, OOP is powerful not because it’s fancy — but because it mirrors real life.

We don’t think in terms of disconnected data and global functions. We think in terms of objects — people, cars, books, dogs — all with their own properties and behaviors.

OOP brings that thinking into programming. It gives our code:

  • Structure
  • Scalability
  • Reusability
  • Safety
  • Clarity

So as we grow our projects — from school management systems to massive software — OOP helps us stay organized and sane.