Object Oriented Programming
⚠️ Heads up: Everything I share here is based on how I’ve understood the concepts as a student. If anything seems off or confusing, feel free to reach out or cross-check it elsewhere. Learning is a two-way journey!

Back in the early days of coding, most programs were written using something called procedural programming. This simply meant writing step-by-step instructions, like following a recipe. You define functions, give them data, and run them in order.

While that worked fine for small programs, it got tricky as things got bigger. Since the code and the data it used were handled separately, managing complex programs became messy, especially when multiple parts needed to use or change the same data.

To fix this, programmers came up with a better approach called Object-Oriented Programming (OOP). It groups both data and related functions together into something called an object. This makes the code easier to organize, reuse, and understand.

Classes

A class is like a blueprint or template. It defines how something should behave and what it should contain — like the attributes (e.g., name, color) and the behaviors (e.g., move, stop) of a car.

Example of Class from real world object
A real-world analogy: designing the idea of a car (class)

There are mainly two types of classes you’ll hear about:

Types of Class
Concrete vs Abstract Class Overview

We’ll explore abstract classes more in upcoming sections. 😊

Objects

An object is like a real-world thing created from a class blueprint. It holds actual values and can do things using the functions defined in the class.

Example of Object from real world object
A real-world car object made from the 'Car' class

There are two main ways to create objects in languages like C++:

  1. 1. Stack Object Creation:
    This is when you create the object directly in memory. It gets destroyed automatically when it goes out of scope.
    // C++ example
    class Car {
    public:
        void drive() { /* ... */ }
    };
    
    int main() {
        Car myCar; // Stack object
        myCar.drive();
        // myCar is destroyed automatically
    }
    
  2. 2. Dynamic Object Creation:
    This method gives you more control. You create the object using new and must manually delete it to avoid memory leaks.
    // C++ example
    Car* myCarPtr = new Car(); // Dynamic object
    myCarPtr->drive();
    delete myCarPtr; // You must delete it manually
    

🔁 In short: stack objects are easy to manage, while dynamic objects give you more control but require extra care.

Class Components

A class contains multiple components:

Data Members
Data Members (Attributes)
Member Function
Member Function (Behaviour)
// Method example
class Car {
  public:
    void honk() {
      cout << "Beep!";
    }
};
Constructor
Constructor Example
// Constructor example
class Car {
  public:
    string color;
    Car(string c) {
      color = c;
    }
};
Destructor
Destructor Example
// Destructor example
class Car {
  public:
    ~Car() {
      // cleanup code
    }
};
Access Modifier
Access Modifer and Who can Access Them
// Access Modifiers example
class Car {
  private:
    int speed;
  public:
    void setSpeed(int s) { speed = s; }
};

Inheritance

Inheritance is like passing down traits from parents to children, but in code! It lets you create a new class (called a derived or child class) based on an existing class (the base or parent class). The child class automatically gets all the attributes and methods of the parent, and you can add more or change them as needed.

This helps you avoid repeating code and makes it easier to organize related classes. For example, you can have Mother and Father classes, and then create a Child class that inherits from them.

Inheritance Example with Family
A Child inheriting traits from Mother and Father

Types of Inheritance

The Diamond Problem

Sometimes, multiple inheritance can cause confusion. Imagine both Mother and Father inherit from Grandmother, and then Child inherits from both Mother and Father. Now, Child has two copies of Grandmother! This is called the diamond problem.

Diamond Problem with Family
The Diamond Problem: Child gets two Grandmothers!
// Diamond Problem Example
class Grandmother { public: void cook() { /* ... */ } };
class Mother : public Grandmother { };
class Father : public Grandmother { };
class Child : public Mother, public Father { };
// Child has two copies of Grandmother::cook()

Solving the Diamond Problem

To fix this, C++ lets you use virtual base classes. By declaring the inheritance as virtual, you make sure only one copy of the base class is shared, no matter how many times it appears in the hierarchy.

// Solution with Virtual Base Class
class Grandmother { public: void cook() { /* ... */ } };
class Mother : virtual public Grandmother { };
class Father : virtual public Grandmother { };
class Child : public Mother, public Father { };
// Only one copy of Grandmother in Child

If you ever need to specify which parent's method to use (in case of ambiguity), you can use the scope resolution operator (::):

// Using scope resolution
child.Mother::cook(); // Calls cook() from Mother's Grandmother
child.Father::cook(); // Calls cook() from Father's Grandmother

In summary: inheritance helps you reuse code, but be careful with multiple inheritance! Virtual base classes and the scope resolution operator help you avoid confusion and keep things tidy—even in a big family tree.

Quick Recap of OOP Concepts

✨ You can zoom in or out using your mouse scroll wheel, and click and drag the diagram to explore different parts. It’s like moving around a real whiteboard — helpful for revising all the key concepts at your own pace!

OOP Recap