> ## Documentation Index
> Fetch the complete documentation index at: https://resources.devweekends.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Object-Oriented Programming

> Classes, Inheritance, Polymorphism, and the Rule of Five

# Object-Oriented Programming (OOP)

C++ is a multi-paradigm language, but its OOP features are powerful. OOP allows you to model real-world entities (like a `Player`, `Car`, or `File`) as objects that contain both data and behavior.

***

## 1. Classes & Structs

A **Class** is a blueprint for creating objects. It encapsulates data (attributes) and functions (methods) that operate on that data.

In C++, `class` and `struct` are almost identical. The only difference is default visibility:

* **class**: Members are `private` by default. (Used for complex objects with invariants).
* **struct**: Members are `public` by default. (Used for simple data containers).

```cpp theme={null}
class Player {
private:
    // Data (State) - Hidden from the outside world (Encapsulation)
    std::string name;
    int health;

public:
    // Constructor: Initializes the object using a Member Initializer List.
    // Why the initializer list ": name(n), health(h)" instead of assignment in the body?
    // 1. For non-trivial types (like std::string), it avoids default-constructing
    //    then reassigning -- the initializer list constructs directly.
    // 2. For const and reference members, it is the ONLY way to initialize them.
    // 3. It is simply the idiomatic, efficient way in modern C++.
    Player(std::string n, int h) : name(std::move(n)), health(h) {
        // std::move(n) avoids copying the string -- we "steal" n's contents
        // since we took it by value and won't use it again.
    }

    // Method: Defines behavior
    void takeDamage(int amount) {
        health -= amount;
    }

    // Getter: Provides read-only access
    // 'const' means this method will NOT modify the object
    int getHealth() const {
        return health;
    }
};
```

### Access Modifiers

* **public**: Accessible from anywhere. The "interface" of your class.
* **private**: Accessible only from within the class. Used for internal state.
* **protected**: Accessible from the class and its derived classes (children).

### Class vs Struct -- When to Use Which

| Criteria                                                             | Use `class`                                        | Use `struct`                                          |
| :------------------------------------------------------------------- | :------------------------------------------------- | :---------------------------------------------------- |
| Has invariants (health cannot be negative, size must match capacity) | Yes -- private data + public methods enforce rules | No                                                    |
| Has behavior (methods beyond simple getters)                         | Yes                                                | Generally no                                          |
| Is a simple data aggregate (Point, Color, Config)                    | Overkill                                           | Yes -- public members, no methods beyond constructors |
| Needs inheritance or polymorphism                                    | Yes                                                | Rarely (technically works, but signals wrong intent)  |
| Used as a function parameter bundle or return type                   | Sometimes                                          | Yes -- clearly communicates "just data"               |

This is a convention, not a language rule. The compiler treats them identically (except for default access). But conventions matter -- when a reader sees `struct`, they expect plain data. When they see `class`, they expect encapsulated behavior.

***

## 2. Inheritance

Inheritance allows a class to derive properties and behavior from another. It promotes code reuse.

```cpp theme={null}
// Base Class (Parent)
class Character {
protected:
    int health;
public:
    Character(int h) : health(h) {}
    
    // 'virtual' allows this function to be overridden by children
    virtual void attack() { std::cout << "Generic attack\n"; }
};

// Derived Class (Child)
class Wizard : public Character {
    int mana;
public:
    Wizard(int h, int m) : Character(h), mana(m) {}

    // Override base behavior
    void attack() override {
        std::cout << "Cast fireball! Mana: " << mana << "\n";
    }
};
```

<Tip>
  Always use the `override` keyword when overriding virtual functions. It ensures the compiler checks that you are actually overriding a base method, preventing bugs from typos.
</Tip>

***

## 3. Polymorphism

Polymorphism ("many shapes") allows you to treat derived objects as base objects. This is the magic that lets you write flexible code.

For example, you can have a list of `Character*` pointers, some pointing to `Wizard`s, some to `Warrior`s. When you call `attack()`, the **correct** version is called for each object.

```cpp theme={null}
void performAttack(Character* c) {
    // Dynamic Dispatch: The runtime decides which function to call
    c->attack(); 
}

int main() {
    Wizard w(100, 50);
    Character c(100);

    performAttack(&w); // Prints "Cast fireball..."
    performAttack(&c); // Prints "Generic attack"
}
```

### Virtual Destructors

**Crucial**: If a class has virtual functions, it **must** have a virtual destructor. Without it, deleting a derived object through a base pointer only runs the base destructor -- the derived class's destructor is silently skipped, leaking any resources it manages.

```cpp theme={null}
class Base {
public:
    virtual ~Base() { std::cout << "Base destroyed\n"; }
};

class Derived : public Base {
    std::vector<int> data;  // Imagine this holds 1GB of data
public:
    ~Derived() { std::cout << "Derived destroyed\n"; }
};

// CORRECT: ~Base() is virtual, so both destructors run
Base* b = new Derived();
delete b; // Prints "Derived destroyed" THEN "Base destroyed". All resources freed.

// If ~Base() was NOT virtual:
// delete b; // Only ~Base() runs. Derived's vector is never freed. 1GB leaked!
```

<Warning>
  **This is one of the most common C++ bugs in production code**. If you see a class with a `virtual` method but a non-virtual destructor, it is almost certainly a bug. Modern compilers like Clang will warn about this with `-Wnon-virtual-dtor`. Enable this warning and treat it as an error.
</Warning>

***

## 4. Abstract Classes & Interfaces

A class is **abstract** if it has at least one **pure virtual function** (`= 0`). It cannot be instantiated. This is how C++ implements interfaces. It forces children to implement specific behavior.

```cpp theme={null}
class IDrawable {
public:
    virtual void draw() const = 0; // Pure virtual: "Children MUST implement this"
    virtual ~IDrawable() = default;
};

class Circle : public IDrawable {
public:
    void draw() const override {
        std::cout << "Drawing Circle\n";
    }
};
```

***

## 5. The Rule of Five (and Why You Want Rule of Zero)

In modern C++, if you manage resources manually (like raw pointers), you need to define 5 special member functions to handle copying and moving correctly. If you define any one of them, you almost certainly need all five -- the compiler's auto-generated defaults will do the wrong thing for the others.

1. **Destructor**: Cleans up resources.
2. **Copy Constructor**: Creates a new object from an existing one (Deep Copy).
3. **Copy Assignment Operator**: Assigns an existing object to another (Deep Copy).
4. **Move Constructor** (C++11): "Steals" resources from a temporary object (Performance).
5. **Move Assignment Operator** (C++11): Assigns by stealing resources.

Think of it like moving apartments. **Copying** is hiring movers to buy identical furniture for the new apartment. **Moving** is loading your existing furniture onto a truck and driving it to the new place -- much cheaper, but the old apartment is left empty.

```cpp theme={null}
class Buffer {
    int* data;
    size_t size;

public:
    // Constructor
    Buffer(size_t sz) : size(sz), data(new int[sz]) {}

    // 1. Destructor -- release the resource
    ~Buffer() { delete[] data; }

    // 2. Copy Constructor -- deep copy (expensive but safe)
    Buffer(const Buffer& other) : size(other.size), data(new int[other.size]) {
        std::copy(other.data, other.data + size, data);
    }

    // 3. Copy Assignment Operator -- deep copy with self-assignment safety
    Buffer& operator=(const Buffer& other) {
        if (this != &other) {           // Guard against self-assignment: buf = buf
            delete[] data;              // Free old resource
            size = other.size;
            data = new int[size];       // Allocate new resource
            std::copy(other.data, other.data + size, data);
        }
        return *this;
    }

    // 4. Move Constructor -- steal resources (cheap!)
    Buffer(Buffer&& other) noexcept : data(other.data), size(other.size) {
        other.data = nullptr;  // Leave the source in a valid but empty state
        other.size = 0;
    }

    // 5. Move Assignment Operator -- steal resources on assignment
    Buffer& operator=(Buffer&& other) noexcept {
        if (this != &other) {
            delete[] data;             // Free our old resource
            data = other.data;         // Steal theirs
            size = other.size;
            other.data = nullptr;      // Leave source empty
            other.size = 0;
        }
        return *this;
    }
};
```

<Note>
  **Rule of Zero -- the real goal**: If your class only uses RAII types (like `std::string`, `std::vector`, `std::unique_ptr`), you don't need to write ANY of these five functions. The compiler-generated defaults do the right thing automatically. This is not just a shortcut -- it is the recommended practice. If you find yourself writing a destructor, ask: "Can I use a smart pointer or standard container instead?" The answer is almost always yes.
</Note>

### Composition Over Inheritance

A quick but important design note: prefer **composition** (having objects as members) over **inheritance** (deriving from base classes). Inheritance creates tight coupling -- changes to the base class ripple through every derived class. Composition is more flexible.

```cpp theme={null}
// Inheritance approach: "A Car IS an Engine" -- this is wrong
class Car : public Engine { /* ... */ };

// Composition approach: "A Car HAS an Engine" -- this is correct
class Car {
    Engine engine;         // Car owns an Engine
    Transmission trans;    // Car owns a Transmission
public:
    void start() { engine.ignite(); }
};
```

Use inheritance for true "is-a" relationships (a `Wizard` is a `Character`) and when you need polymorphism. Use composition for everything else.

### Inheritance vs Composition -- Decision Framework

| Question to ask                                                                          | If Yes                                            | If No                                    |
| :--------------------------------------------------------------------------------------- | :------------------------------------------------ | :--------------------------------------- |
| Is the relationship truly "is-a"? (A Dog IS an Animal)                                   | Inheritance is appropriate                        | Use composition                          |
| Do you need runtime polymorphism (virtual dispatch)?                                     | Inheritance is the standard mechanism             | Composition or templates work better     |
| Does the derived class need to substitute for the base everywhere? (Liskov Substitution) | Inheritance is correct                            | Forced inheritance will cause bugs       |
| Might the "base" be swapped out at runtime? (different engines, strategies)              | Composition -- inject the dependency              | Inheritance locks you in at compile time |
| Are you combining capabilities from multiple sources?                                    | Composition (C++ multiple inheritance is fragile) | Either approach can work                 |

***

## 6. OOP Gotchas That Cause Real Bugs

**Gotcha 1: Object Slicing**

When you assign a derived object to a base variable *by value*, the derived part is "sliced off." Only the base portion is copied. This is silent and almost never what you want.

```cpp theme={null}
Wizard w(100, 50);
Character c = w;   // SLICING! Only the Character part of w is copied.
c.attack();        // Calls Character::attack(), not Wizard::attack().
                   // The Wizard's mana and overridden behavior are gone.
```

The fix: always use references or pointers when working with polymorphic objects. `Character& c = w;` preserves the full Wizard identity.

**Gotcha 2: The Diamond Problem**

When a class inherits from two classes that share a common base, the common base's members appear twice in the derived object. This causes ambiguity.

```cpp theme={null}
class Animal { public: int age; };
class Dog : public Animal {};
class Cat : public Animal {};
class DogCat : public Dog, public Cat {};
// DogCat has TWO copies of Animal::age -- Dog::age and Cat::age
// dc.age is ambiguous -- which one?
```

The fix: use `virtual` inheritance (`class Dog : virtual public Animal`) so only one copy of `Animal` exists. But virtual inheritance has overhead (extra pointer per object) and makes construction order complex. The real fix in most cases is to redesign so you do not need multiple inheritance from concrete classes.

**Gotcha 3: Calling Virtual Functions in Constructors**

Inside a constructor, virtual dispatch does NOT call the derived class's override. The object is still being constructed, so the derived part does not exist yet. The base class version is always called.

```cpp theme={null}
class Base {
public:
    Base() { init(); }          // Calls Base::init(), even for Derived objects
    virtual void init() { std::cout << "Base init\n"; }
};
class Derived : public Base {
public:
    void init() override { std::cout << "Derived init\n"; }
};

Derived d;  // Prints "Base init" -- NOT "Derived init"!
```

This is specified behavior, not a bug in the compiler. If you need polymorphic initialization, use a factory function that constructs the object and then calls an `init()` method separately.

***

## Exercises

1. **Slicing detector**: Create a `Shape` base class with `area()` and a `Circle` derived class. Write a function that takes a `Shape` by value and another that takes a `Shape&`. Pass a `Circle` to both. Observe the difference. Add a `print` statement in each `area()` to prove which version is called.
2. **Rule of Five practice**: Implement a simple `String` class that wraps a `char*` buffer. Implement all five special member functions. Verify your move constructor works by returning a `String` from a function and checking that no deep copy occurs (add print statements to each special member function).
3. **Design challenge**: You are building a notification system that can send alerts via Email, SMS, Slack, and Webhook. Should you use inheritance (a `Notifier` base class with derived classes) or composition (a `NotificationService` that holds a `Transport` strategy)? Sketch both designs. Which is easier to extend when product asks for a PagerDuty integration next month?
4. **Virtual destructor audit**: Take any class hierarchy you have written and deliberately remove the `virtual` keyword from the base destructor. Delete a derived object through a base pointer. Compile with `-Wnon-virtual-dtor` and observe the warning. Run under AddressSanitizer and observe the runtime behavior.

***

## Summary

* **Classes**: Encapsulate data and behavior.
* **Inheritance**: Enables code reuse (`public` inheritance is "is-a").
* **Polymorphism**: Allows dynamic behavior via Virtual Functions.
* **Abstract Classes**: Define interfaces/contracts.
* **Rule of Five**: Implement copy/move semantics only if managing raw resources. Otherwise, follow **Rule of Zero**.

Next, we'll explore the **Standard Template Library (STL)**, which provides powerful containers and algorithms so you don't have to write them from scratch.
