8000 system_design/inheritance at main Β· Parihar07/system_design Β· GitHub
[go: up one dir, main page]

8000
Skip to content

Latest commit

Β 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

README.md

Inheritance - Interview Preparation

οΏ½ Table of Contents

Core Topics (10 Parts)

  1. Part 1: Inheritance Basics
  2. Part 2: Types of Inheritance
  3. Part 3: Access Control in Inheritance
  4. Part 4: Constructor & Destructor in Inheritance
  5. Part 5: Function Overriding
  6. Part 6: Virtual Functions & Polymorphism
  7. Part 7: Abstract Classes & Interfaces
  8. Part 8: Multiple Inheritance & Diamond Problem
  9. Part 9: Real-World Examples
  10. Part 10: Interview Questions

Deep Dive Sections

Extra Knowledge


πŸ“š Topics Covered

Part 1: Inheritance Basics βœ…

  • What is Inheritance?
  • Why use Inheritance? (Code reuse, IS-A relationship)
  • Base class and Derived class
  • Access specifiers in inheritance (public, protected, private)
  • File: 01_inheritance_basics.cpp

Part 2: Types of Inheritance βœ…

  • Single Inheritance
  • Multiple Inheritance
  • Multilevel Inheritance
  • Hierarchical Inheritance
  • Hybrid Inheritance
  • Diamond Problem
  • Virtual Inheritance (Solution)
  • File: 02_types_of_inheritance.cpp

Part 3: Access Control in Inheritance βœ…

  • Public inheritance (IS-A relationship)
  • Protected inheritance
  • Private inheritance (HAS-A implementation)
  • Access specifier transformation rules
  • File: 03_access_control.cpp

Part 4: Constructor & Destructor in Inheritance βœ…

  • Order of constructor calls (Base β†’ Derived)
  • Order of destructor calls (Derived β†’ Base)
  • Calling base class constructors
  • Virtual destructors (IMPORTANT!)
  • File: 04_constructor_destructor_order.cpp

Part 5: Function Overriding βœ…

Part 6: Virtual Functions & Polymorphism βœ…

  • Virtual functions
  • Pure virtual functions (Abstract classes)
  • Virtual table (vtable)
  • Runtime polymorphism
  • File: 06_virtual_functions.cpp

Part 7: Abstract Classes & Interfaces βœ…

  • Pure virtual functions
  • Abstract classes (cannot instantiate)
  • Interfaces in C++
  • Real-world examples
  • File: 07_abstract_classes.cpp

Part 8: Multiple Inheritance & Diamond Problem βœ…

  • Multiple inheritance challenges
  • Diamond problem
  • Virtual inheritance (solution)
  • Real-world use cases
  • File: 08_multiple_inheritance.cpp

Part 9: Real-World Examples βœ…

  • GUI Toolkit Example (UIWidget, Button, TextField)
  • Demonstrates polymorphic rendering engine
  • Shows extensibility through inheritance
  • File: 09_real_world_example.cpp

Part 10: Interview Questions βœ…

  • Diamond Problem and virtual inheritance
  • Virtual destructors and memory leaks
  • Static vs Dynamic dispatch (vptr/vtable)
  • Private inheritance vs Composition
  • Abstract classes and API contracts
  • See the Deep Dive Sections and Extra Knowledge sections above

πŸ’‘ Real-World Example Deep Dive: GUI Toolkit

This is a classic example of using polymorphism to build an extensible system.

#include <iostream>
#include <vector>
#include <string>

// 1. The Abstract Contract (The Interface)
class UIWidget {
public:
    virtual void draw() const = 0; // All widgets must be drawable
    virtual ~UIWidget() = default;
};

// 2. Concrete Implementations
class Button : public UIWidget {
private:
    std::string m_label;
public:
    Button(const std::string& label) : m_label(label) {}
    void draw() const override {
        std::cout << "Drawing a Button: [" << m_label << "]" << std::endl;
    }
};

class TextField : public UIWidget {
private:
    std::string m_text;
public:
    TextField(const std::string& text = "") : m_text(text) {}
    void draw() const override {
        std::cout << "Drawing a TextField: |" << m_text << "|" << std::endl;
    }
};

// 3. The Rendering Engine (The System)
// This function is completely decoupled from the concrete widgets.
void render(const std::vector<UIWidget*>& widgets) {
    std::cout << "\n--- SCREEN REFRESH ---" << std::endl;
    for (const auto* widget : widgets) {
        widget->draw(); // Dynamic dispatch happens here
    }
    std::cout << "----------------------" << std::endl;
}

int main() {
    Button ok_button("OK");
    TextField name_field("Enter name");

    // The rendering engine works with a list of base class pointers.
    std::vector<UIWidget*> widget_list = {&ok_button, &name_field};
    render(widget_list);
    return 0;
}

System-Level Takeaway: The render function represents a stable, core part of a larger system. It can handle any UIWidget without ever needing to be modified. This is achieved by programming to an interface (UIWidget) rather than an implementation (Button, TextField).


πŸ’‘ Extra Knowledge - Common Confusions

Private Inheritance vs Final Keyword

⚠️ IMPORTANT: These are DIFFERENT concepts!

1. Private Inheritance (Hiding Base Interface)

class GPS {
public:
    void showLocation() { }
};

class SmartPhone : private GPS {  // Private inheritance
    // GPS interface hidden from outside
};

class AdvancedPhone : public SmartPhone {
    // βœ… ALLOWED! Can still inherit from SmartPhone
    // ❌ But can't access GPS members
};

Purpose:

  • Hides base class interface from outside world
  • Implementation detail (HAS-A relationship)
  • Does NOT prevent further inheritance
  • Further classes can inherit, but can't access private base

2. Final Keyword (Preventing Inheritance)

class Base {
public:
    void baseMethod() { }
};

class Derived final : public Base {  // ← final keyword
    // This is the LAST class in hierarchy
};

class FurtherDerived : public Derived {
    // ❌ ERROR! Cannot inherit from final class
    // Compilation error!
};

Purpose:

  • Completely prevents ANY inheritance
  • No class can derive from a final class
  • Used for: Security, performance optimization, design enforcement

When to Use Each?

Scenario Use
Hide implementation details (HAS-A) Private Inheritance
Prefer composition over private inheritance βœ… Composition (better!)
Stop ALL inheritance completely final keyword
Allow inheritance but hide base members Private Inheritance
Performance critical class (no vtable) final keyword

Real-World Examples:

// Private Inheritance (rare, prefer composition)
class Stack : private std::vector<int> {
    // vector is implementation detail
    // But other classes can inherit from Stack
};

// Final keyword (prevent inheritance)
class String final {
    // No one should inherit from String
    // It's a complete, sealed class
};

// Java example: public final class String
// C# example: public sealed class String

Interview Tip: If asked "How to prevent inheritance };

// Dog inherits eat() from Animal // Dog IS-A Animal


### Why Use Inheritance?

1. **Code Reuse** - Don't repeat common code
2. **Logical Hierarchy** - Model real-world relationships
3. **Extensibility** - Add features without changing base
4. **Polymorphism** - Treat derived objects as base objects

### IS-A vs HAS-A Relationship

```cpp
// IS-A relationship (Inheritance)
class Car : public Vehicle { };  // Car IS-A Vehicle

// HAS-A relationship (Composition)
class Car {
    Engine engine;  // Car HAS-A Engine
};

Rule of thumb: Use inheritance for IS-A, composition for HAS-A


πŸ” How to Identify When to Use Inheritance

The IS-A Test (Primary Indicator)

Question to ask: "Is X a type of Y?"

βœ… YES β†’ Use Inheritance
- Dog IS-A Animal βœ“
- Coffee IS-A Drink βœ“
- Manager IS-A Employee βœ“
- SavingsAccount IS-A BankAccount βœ“

❌ NO β†’ Use Composition (or no relationship)
- Car IS-A Engine? βœ— (Car HAS-A Engine)
- House IS-A Door? βœ— (House HAS-A Door)
- Student IS-A Book? βœ— (Student HAS-A Book)

How to Identify Base Class

Step 1: Find Common Characteristics

Example: Analyzing Coffee Shop Drinks

Entities: Coffee, Tea, Juice, Smoothie

Common properties:
- All have a name
- All have a price
- All have a temperature
- All can be served

Common concept: DRINK

Decision: Create "Drink" as base class βœ“

Step 2: Apply IS-A Test

Coffee IS-A Drink? βœ“
Tea IS-A Drink? βœ“
Juice IS-A Drink? βœ“
Smoothie IS-A Drink? βœ“

Conclusion: "Drink" should be base class

Step 3: Design Hierarchy

class Drink {  // Base class - common behavior
protected:
    string name;
    double price;
    int temperature;
};

class Coffee : public Drink { };  // Specific type
class Tea : public Drink { };
class Juice : public Drink { };

Decision Tree

Start: Multiple similar entities exist
          ↓
Question: Do they share characteristics?
          ↓
        YES
          ↓
Question: "X IS-A Y" - Natural statement?
          ↓
    β”Œβ”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”
   YES         NO
    ↓           ↓
Create      Use Composition
Base Class  or Functions
    ↓
Extract      Design hierarchy:
common       Base (general)
features        ↓
into base    Derived (specific)

Real-World Examples

Example 1: Vehicle System

Observation: Car, Bike, Truck exist

Step 1: Find commonality
- All have brand, year
- All can start(), stop()
- All move on roads

Step 2: IS-A test
- Car IS-A Vehicle? βœ“
- Bike IS-A Vehicle? βœ“
- Truck IS-A Vehicle? βœ“

Step 3: Identify base
Base class: Vehicle (common concept)

Result:
class Vehicle { };      // Base
class Car : public Vehicle { };
class Bike : public Vehicle { };

Example 2: Employee Hierarchy

Observation: Manager, Developer, Intern exist

Step 1: Find commonality
- All have name, ID, salary
- All work for company
- All have common HR processes

Step 2: IS-A test
- Manager IS-A Employee? βœ“
- Developer IS-A Employee? βœ“
- Intern IS-A Employee? βœ“

Step 3: Identify base
Base class: Employee

Result:
class Employee { };     // Base
class Manager : public Employee { };
class Developer : public Employee { };

Example 3: Counter-Example (NO Inheritance)

Observation: Car and Engine exist

Step 1: Relationship?
- Car uses Engine
- Car contains Engine

Step 2: IS-A test
- Car IS-A Engine? βœ— (Makes no sense!)

Step 3: Alternative
Use composition (HAS-A):

class Car {
    Engine engine;  // Car HAS-A Engine
};

When NOT to Use Inheritance

❌ Don't use inheritance if:

  1. No IS-A relationship

    // ❌ WRONG
    class Car : public Engine { };  // Car IS-A Engine? No!
  2. Just for code reuse

    // ❌ WRONG - Using inheritance just to reuse utility methods
    class MyClass : public UtilityFunctions { };
    
    // βœ“ CORRECT - Use composition or helper functions
    class MyClass {
        UtilityFunctions utils;  // Has utility
    };
  3. Violates Liskov Substitution Principle

    // ❌ WRONG - Square is NOT a proper Rectangle subtype
    class Square : public Rectangle { };
    // Problem: Setting width/height independently breaks for Square
  4. Creates tight coupling

    // ❌ WRONG - Implementation inheritance for convenience
    class ArrayList : public Vector { };  // Too tightly coupled

Checklist: Should I Use Inheritance?

β˜‘ Does "X IS-A Y" make logical sense?
β˜‘ Are there shared properties/behaviors?
β˜‘ Do I need polymorphism (treat X as Y)?
β˜‘ Is there a natural hierarchy?
β˜‘ Can derived class be substituted for base class?

If 3+ checks pass β†’ Use Inheritance βœ“
If less than 3 β†’ Consider Composition or other design

Protected vs Private in Base Class

When designing base class:

class Base {
private:
    int internalDetail;  // Implementation detail, hide from everyone
    
protected:
    int sharedData;      // Derived classes need access
    
    void helperMethod() {  // Derived classes can use
        // ...
    }
    
public:
    void publicInterface() {  // Everyone can use
        // ...
    }
};

Guidelines:

  • private: Internal implementation, derived classes don't need
  • protected: Data/methods that derived classes need to access or override
  • public: Interface for all users (including derived classes)

Common pattern:

class BankAccount {
private:
    double balance;  // Keep private (sensitive)
    
protected:
    // Provide controlled access for derived classes
    double getBalance() const { return balance; }
    void setBalance(double b) { balance = b; }
    
public:
    void deposit(double amount);  // Public interface
};

🧠 Understanding Object Creation in Inheritance

The Key Question: What Happens in Memory?

Common Misconception: "When derived object is created, does it reference a separate base object?"

Reality: There is ONE single object containing both base and derived parts!

Memory Layout

class Base {
    int baseVar;
};

class Derived : public Base {
    int derivedVar;
};

Derived d;  // What's in memory?

Visual Representation:

❌ WRONG: Two separate objects
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Base object β”‚ ← baseVar
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
       ↑ reference?
       β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Derived obj β”‚ ← derivedVar
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜


βœ… CORRECT: One object with two parts
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Derived Object 'd' β”‚  ← ONE object in memory
β”‚                     β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚ Base Part     β”‚  β”‚ ← baseVar (constructed first)
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                     β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚ Derived Part  β”‚  β”‚ ← derivedVar (constructed second)
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Creation Process

Step 1: Memory Allocation
  - Allocate space for ENTIRE object
  - Size = sizeof(Base) + sizeof(Derived's own data)

Step 2: Base Constructor Runs
  - Initialize base class members
  - Base part is NOW ready

Step 3: Derived Constructor Runs
  - Initialize derived class members
  - Derived part is NOW ready

Result: ONE complete object with both parts

Code Demonstration

class Animal {
protected:
    string name;
public:
    Animal(string n) : name(n) {
        cout << "Animal constructor: " << this << endl;
    }
};

class Dog : public Animal {
private:
    string breed;
public:
    Dog(string n, string b) : Animal(n), breed(b) {
        cout << "Dog constructor: " << this << endl;
        // Notice: SAME memory address!
    }
};

Dog d("Buddy", "Golden");
// Output shows SAME address for both constructors
// Proof: It's ONE object!

Key Points

  1. βœ… Single Object: Only ONE object exists in memory
  2. βœ… Embedded Parts: Base part is embedded INSIDE derived object
  3. βœ… Contiguous Memory: All parts are in sequential memory locations
  4. βœ… Construction Order: Base first, then derived (within same object)
  5. βœ… No References: No pointers/references between base and derived parts
  6. βœ… Size: sizeof(Derived) >= sizeof(Base) + sizeof(Derived data)

Analogy: House with Floors

Think of derived object as a house with multiple floors:

House (Derived Object)
β”œβ”€β”€ Ground Floor (Base part)   ← Built first
β”‚   └── Foundation, structure
└── Upper Floor (Derived part) ← Built on top
    └── Additional rooms

❌ NOT two separate buildings!
βœ… ONE building with two floors!

Multiple Inheritance Case

class A { int a; };
class B { int b; };
class C : public A, public B { int c; };

C obj;  // Memory layout:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Object 'obj' (type C)     β”‚  ← Still ONE object!
β”‚                             β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚   β”‚ A Part              β”‚   β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚   β”‚ B Part              β”‚   β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚   β”‚ C Part              β”‚   β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Interview Answer

Q: When a derived class object is created, is there a separate base class object?

A: No. When a derived object is created:

  • Only ONE object exists in memory
  • It contains the base class part embedded within it
  • The base constructor runs first to initialize the base part
  • Then the derived constructor runs to initialize the derived part
  • All parts are in contiguous memory (not separate objects)
  • There are no references or pointers between parts - they're physically part of the same object

Think of it as a single object with layers, not separate connected objects.


πŸŽ“ Interview Q&A

Q1: What is inheritance?

Inheritance is an OOP mechanism where a derived class acquires properties and methods from a base class, enabling code reuse and establishing IS-A relationships.

Q2: What are the types of inheritance in C++?

Single, Multiple, Multilevel, Hierarchical, and Hybrid inheritance. C++ supports all types, including multiple inheritance (unlike Java).

Q3: What is the difference between public, protected, and private inheritance?

  • Public: Base public members remain public in derived (IS-A relationship)
  • Protected: Base public members become protected in derived
  • Private: Base public/protected members become private in derived (HAS-A implementation)

Q4: Why should base class destructor be virtual?

When deleting a derived object through a base pointer, without virtual destructor, only the base destructor is called β†’ memory leak. Virtual destructor ensures proper cleanup of derived class resources.

Q5: What is the diamond problem?

In multiple inheritance, if two base classes inherit from a common grandparent, the derived class has two copies of grandparent members. Solution: Virtual inheritance.

Q6: What is function overriding?

When a derived class provides a specific implementation of a function already defined in the base class. Requires same function signature. Use virtual for runtime polymorphism.

Q7: What's the difference between overloading and overriding?

  • Overloading: Same function name, different parameters, compile-time (same class)
  • Overriding: Same function signature, different implementation, runtime (inheritance)

Q8: What is a pure virtual function?

A virtual function with = 0 that has no implementation in base class. Makes the class abstract (cannot be instantiated). Derived classes must override it.

Q9: When to use inheritance vs composition?

Use inheritance for IS-A relationships (natural hierarchy). Use composition for HAS-A relationships (building complex objects from simpler ones). Prefer composition over inheritance when in doubt.

Q10: What is virtual inheritance?

A technique to solve the diamond problem in multiple inheritance. Uses virtual keyword in inheritance declaration to ensure only one copy of common base class.


πŸ“Œ Best Practices

  1. βœ… Use public inheritance for IS-A relationships
  2. βœ… Make base class destructor virtual (if using polymorphism)
  3. βœ… Prefer composition over inheritance (when relationship isn't clear)
  4. βœ… Use override keyword (C++11) for clarity
  5. βœ… Keep inheritance hierarchies shallow (avoid deep nesting)
  6. βœ… Use abstract classes for interfaces (pure virtual functions)
  7. βœ… Avoid multiple inheritance (unless necessary)
  8. βœ… Call base constructor explicitly (in derived class initialization list)

πŸ” Coming Up

We'll explore:

  • How inheritance enables polymorphism
  • Virtual function tables (vtable)
  • Abstract classes and interfaces
  • Real-world design patterns using inheritance
  • Common pitfalls and how to avoid them

Ready to dive in? Let's start with Part 1: Inheritance Basics! πŸš€


Part 6.1: Deep Dive - The vptr and vtable Visual Map

This is the internal mechanism that makes runtime polymorphism possible. Once you understand this, you'll never forget how virtual functions work.

The Core Concepts

  1. vtable (Virtual Table):

    • What: A static array of function pointers.
    • Who: One vtable exists per class that has at least one virtual function.
    • When: Created at compile time.
    • Where: Stored in a read-only segment of memory. All objects of the same class share this single vtable.
  2. vptr (Virtual Pointer):

    • What: A hidden pointer. It's a member variable secretly added by the compiler.
    • Who: One vptr exists per object of a class with virtual functions.
    • When: The vptr is initialized during object construction (in the constructor).
    • Job: To point to the correct vtable for that object's class.

Visualizing the Memory Layout

Let's use these classes:

class Shape {
public:
    virtual void draw();
    virtual void rotate();
    // vptr is added here by the compiler
};

class Circle : public Shape {
public:
    void draw() override; // Overrides Shape::draw
    // rotate() is inherited from Shape
};

At Compile Time: The compiler builds the vtables.

Shape's vtable:

  • [0] -> &Shape::draw()
  • [1] -> &Shape::rotate()

Circle's vtable:

  • [0] -> &Circle::draw() (Overridden)
  • [1] -> &Shape::rotate() (Inherited)

At Runtime: When you create an object, it gets its vptr.

Shape* s = new Circle();

This is what happens in memory:

// In the HEAP
Circle Object (at address 0x1000)
  - vptr ---------------------> // In READ-ONLY MEMORY
  - (other data members...)      // Circle's vtable
                                 //   [0]: &Circle::draw()
                                 //   [1]: &Shape::rotate()

The object's vptr is set to point to the vtable of its actual type (Circle).

The Magic: Step-by-Step Virtual Function Call

When the code s->draw(); is executed:

  1. Follow the vptr: The program first looks inside the object s points to. It finds the hidden vptr. [Circle Object] -> vptr

  2. Go to the vtable: It follows the vptr to the Circle's vtable. vptr -> [Circle's vtable]

  3. Look up the Function: The compiler knows draw() is the first virtual function (at index [0]). It looks up the address at vtable[0]. [Circle's vtable] -> [0] -> &Circle::draw()

  4. Call the Function: The program calls the function at that address. In this case, it's Circle::draw().

This is Dynamic Dispatch. The decision of which draw() to call is made at RUNTIME, based on the actual type of the object, not the type of the pointer.

What if the function is NOT virtual?

If you call a non-virtual function, like s->regularFunc();:

  1. Check Pointer Type: The compiler sees that s is a Shape*.
  2. Direct Call: It generates a direct, hard-coded call to Shape::regularFunc().
  3. No vptr/vtable is ever used. This is Static Dispatch.

Summary: The Unforgettable Map

// OBJECTS (in RAM/Heap)                // VTABLES (in Read-Only Memory)

Circle obj1:
  vptr ----------------------------> Circle's vtable:
                                        - &Circle::draw()
Circle obj2:                            - &Shape::rotate()
  vptr --------------------|
                           |
Rectangle obj1:            |
  vptr --------------------|------> Rectangle's vtable:
                                        - &Rectangle::draw()
                                        - &Rectangle::rotate()

// Key Takeaways:
// 1. Each OBJECT has its OWN vptr.
// 2. All objects of the SAME CLASS share ONE vtable.
// 3. The call `obj->draw()` means:
//    "Follow my vptr, find the draw() entry, and call that function."

This map shows that no matter how many Circle objects you create, they all share one vtable, but each has its own vptr to 2D0A find it. This is the elegant and efficient solution C++ uses for runtime polymorphism.

0