cpp

Virtual Inheritance

02_Virtual_Inheritance⚙️
/**
 * ============================================================
 * C++ VIRTUAL INHERITANCE & DIAMOND PROBLEM
 * ============================================================
 * 
 * This file covers:
 * - Multiple inheritance
 * - The Diamond Problem
 * - Virtual inheritance solution
 * - Virtual base class initialization
 * - Practical examples
 * 
 * Compile: g++ -std=c++17 -Wall 01_virtual_inheritance.cpp -o virtual_inh
 * Run: ./virtual_inh
 * 
 * ============================================================
 */

#include <iostream>
#include <string>
#include <memory>

using namespace std;

// ============================================================
// PART 1: MULTIPLE INHERITANCE BASICS
// ============================================================

class Printable {
public:
    virtual void print() const = 0;
    virtual ~Printable() = default;
};

class Serializable {
public:
    virtual string serialize() const = 0;
    virtual ~Serializable() = default;
};

// Multiple inheritance - inheriting from multiple classes
class Document : public Printable, public Serializable {
private:
    string content;
    
public:
    Document(const string& c) : content(c) {}
    
    void print() const override {
        cout << "Document: " << content << endl;
    }
    
    string serialize() const override {
        return "DOC:" + content;
    }
};

// ============================================================
// PART 2: THE DIAMOND PROBLEM (WITHOUT VIRTUAL)
// ============================================================

namespace DiamondProblem {

// Base class
class Animal {
protected:
    string name;
    int age;
    
public:
    Animal(const string& n = "Unknown", int a = 0) : name(n), age(a) {
        cout << "  Animal constructor: " << name << endl;
    }
    
    virtual ~Animal() {
        cout << "  Animal destructor: " << name << endl;
    }
    
    void eat() const {
        cout << name << " is eating" << endl;
    }
    
    string getName() const { return name; }
    int getAge() const { return age; }
};

// Intermediate classes
class Mammal : public Animal {  // Note: NOT virtual
public:
    Mammal(const string& n, int a) : Animal(n, a) {
        cout << "  Mammal constructor" << endl;
    }
    
    ~Mammal() {
        cout << "  Mammal destructor" << endl;
    }
    
    void nurse() const {
        cout << name << " is nursing" << endl;
    }
};

class WingedAnimal : public Animal {  // Note: NOT virtual
public:
    WingedAnimal(const string& n, int a) : Animal(n, a) {
        cout << "  WingedAnimal constructor" << endl;
    }
    
    ~WingedAnimal() {
        cout << "  WingedAnimal destructor" << endl;
    }
    
    void fly() const {
        cout << name << " is flying" << endl;
    }
};

// Diamond: Bat inherits from both Mammal and WingedAnimal
// This creates TWO copies of Animal!
class Bat : public Mammal, public WingedAnimal {
public:
    // Must construct BOTH Animal bases
    Bat(const string& n, int a) 
        : Mammal(n, a), WingedAnimal(n, a) {
        cout << "  Bat constructor" << endl;
    }
    
    ~Bat() {
        cout << "  Bat destructor" << endl;
    }
    
    void echolocate() const {
        cout << "Bat is using echolocation" << endl;
    }
    
    // Ambiguity! Must specify which path
    void displayName() const {
        cout << "Mammal path: " << Mammal::getName() << endl;
        cout << "WingedAnimal path: " << WingedAnimal::getName() << endl;
    }
};

} // namespace DiamondProblem

// ============================================================
// PART 3: SOLUTION - VIRTUAL INHERITANCE
// ============================================================

namespace VirtualSolution {

// Base class
class Animal {
protected:
    string name;
    int age;
    
public:
    Animal(const string& n = "Unknown", int a = 0) : name(n), age(a) {
        cout << "  [V] Animal constructor: " << name << endl;
    }
    
    virtual ~Animal() {
        cout << "  [V] Animal destructor: " << name << endl;
    }
    
    void eat() const {
        cout << name << " is eating" << endl;
    }
    
    string getName() const { return name; }
    int getAge() const { return age; }
};

// Virtual inheritance - only ONE copy of Animal
class Mammal : virtual public Animal {  // VIRTUAL keyword
public:
    Mammal(const string& n, int a) : Animal(n, a) {
        cout << "  [V] Mammal constructor" << endl;
    }
    
    ~Mammal() {
        cout << "  [V] Mammal destructor" << endl;
    }
    
    void nurse() const {
        cout << name << " is nursing" << endl;
    }
};

class WingedAnimal : virtual public Animal {  // VIRTUAL keyword
public:
    WingedAnimal(const string& n, int a) : Animal(n, a) {
        cout << "  [V] WingedAnimal constructor" << endl;
    }
    
    ~WingedAnimal() {
        cout << "  [V] WingedAnimal destructor" << endl;
    }
    
    void fly() const {
        cout << name << " is flying" << endl;
    }
};

// Now Bat has only ONE copy of Animal!
class Bat : public Mammal, public WingedAnimal {
public:
    // Most derived class MUST initialize virtual base!
    Bat(const string& n, int a) 
        : Animal(n, a),      // Must call Animal directly!
          Mammal(n, a),      // Animal() in Mammal won't be called
          WingedAnimal(n, a) // Animal() in WingedAnimal won't be called
    {
        cout << "  [V] Bat constructor" << endl;
    }
    
    ~Bat() {
        cout << "  [V] Bat destructor" << endl;
    }
    
    void echolocate() const {
        cout << getName() << " is using echolocation" << endl;
    }
    
    // No ambiguity now!
    void displayInfo() const {
        cout << "Name: " << name << ", Age: " << age << endl;
    }
};

} // namespace VirtualSolution

// ============================================================
// PART 4: PRACTICAL EXAMPLE - GUI FRAMEWORK
// ============================================================

class Widget {
protected:
    int x, y;
    int width, height;
    bool visible;
    
public:
    Widget(int x = 0, int y = 0, int w = 100, int h = 100)
        : x(x), y(y), width(w), height(h), visible(true) {
        cout << "  Widget constructor at (" << x << "," << y << ")" << endl;
    }
    
    virtual ~Widget() = default;
    
    virtual void draw() const {
        cout << "Drawing widget at (" << x << "," << y << ")" << endl;
    }
    
    void show() { visible = true; }
    void hide() { visible = false; }
    bool isVisible() const { return visible; }
};

// Virtual inheritance for intermediate classes
class Clickable : virtual public Widget {
protected:
    bool enabled;
    
public:
    Clickable(int x, int y, int w, int h)
        : Widget(x, y, w, h), enabled(true) {
        cout << "  Clickable constructor" << endl;
    }
    
    virtual void onClick() {
        if (enabled) cout << "Widget clicked!" << endl;
    }
    
    void setEnabled(bool e) { enabled = e; }
};

class Scrollable : virtual public Widget {
protected:
    int scrollX, scrollY;
    
public:
    Scrollable(int x, int y, int w, int h)
        : Widget(x, y, w, h), scrollX(0), scrollY(0) {
        cout << "  Scrollable constructor" << endl;
    }
    
    virtual void scroll(int dx, int dy) {
        scrollX += dx;
        scrollY += dy;
        cout << "Scrolled to (" << scrollX << "," << scrollY << ")" << endl;
    }
};

// ScrollableButton inherits from both
class ScrollableButton : public Clickable, public Scrollable {
private:
    string label;
    
public:
    ScrollableButton(const string& lbl, int x, int y, int w, int h)
        : Widget(x, y, w, h),        // Initialize virtual base first
          Clickable(x, y, w, h),
          Scrollable(x, y, w, h),
          label(lbl) {
        cout << "  ScrollableButton constructor: " << label << endl;
    }
    
    void draw() const override {
        cout << "Drawing button '" << label << "' at (" 
             << x << "," << y << ")" << endl;
    }
    
    void onClick() override {
        if (enabled) {
            cout << "Button '" << label << "' clicked!" << endl;
        }
    }
};

// ============================================================
// PART 5: IOSTREAM DIAMOND
// ============================================================

/*
 * The C++ iostream library uses virtual inheritance!
 * 
 *              ios_base
 *                 |
 *              basic_ios     (virtual base)
 *              /       \
 *    basic_istream   basic_ostream  (virtual inheritance)
 *              \       /
 *           basic_iostream
 * 
 * This is why cin, cout, and stringstream work correctly!
 */

// ============================================================
// MAIN FUNCTION
// ============================================================

int main() {
    cout << "============================================" << endl;
    cout << "     C++ VIRTUAL INHERITANCE" << endl;
    cout << "============================================" << endl << endl;

    // ========================================================
    // DEMO 1: Multiple Inheritance Basics
    // ========================================================
    
    cout << "--- DEMO 1: MULTIPLE INHERITANCE ---" << endl << endl;
    
    Document doc("Hello World");
    
    // Can use as Printable
    Printable* printable = &doc;
    printable->print();
    
    // Can use as Serializable  
    Serializable* serializable = &doc;
    cout << "Serialized: " << serializable->serialize() << endl;
    
    cout << endl;

    // ========================================================
    // DEMO 2: Diamond Problem
    // ========================================================
    
    cout << "--- DEMO 2: DIAMOND PROBLEM ---" << endl << endl;
    
    cout << "Creating Bat WITHOUT virtual inheritance:" << endl;
    cout << "(Notice: Animal constructor called TWICE!)" << endl << endl;
    
    {
        DiamondProblem::Bat bat("Bruce", 3);
        cout << endl;
        
        // Ambiguity - must specify path
        // bat.eat();  // ERROR: ambiguous
        bat.Mammal::eat();       // Use Mammal's path
        bat.WingedAnimal::eat(); // Use WingedAnimal's path
        
        cout << endl;
        bat.displayName();  // Shows both paths
        
        cout << "\nDestroying bat:" << endl;
    }
    
    cout << "(Notice: Animal destructor called TWICE!)" << endl;
    
    cout << endl;

    // ========================================================
    // DEMO 3: Virtual Inheritance Solution
    // ========================================================
    
    cout << "--- DEMO 3: VIRTUAL INHERITANCE ---" << endl << endl;
    
    cout << "Creating Bat WITH virtual inheritance:" << endl;
    cout << "(Notice: Animal constructor called ONCE!)" << endl << endl;
    
    {
        VirtualSolution::Bat vbat("Stella", 2);
        cout << endl;
        
        // No ambiguity!
        vbat.eat();     // Works fine - only one Animal
        vbat.nurse();   // Mammal method
        vbat.fly();     // WingedAnimal method
        vbat.echolocate();
        
        cout << endl;
        vbat.displayInfo();  // Accesses shared name and age
        
        cout << "\nDestroying bat:" << endl;
    }
    
    cout << "(Notice: Animal destructor called ONCE!)" << endl;
    
    cout << endl;

    // ========================================================
    // DEMO 4: GUI Widget Example
    // ========================================================
    
    cout << "--- DEMO 4: GUI WIDGET ---" << endl << endl;
    
    cout << "Creating ScrollableButton:" << endl;
    cout << "(Widget is virtual base - constructed once)" << endl << endl;
    
    ScrollableButton button("OK", 100, 200, 80, 30);
    
    cout << "\nUsing the button:" << endl;
    button.draw();
    button.onClick();
    button.scroll(10, 20);
    
    // Access Widget through any path
    button.show();
    cout << "Visible: " << (button.isVisible() ? "yes" : "no") << endl;
    
    cout << endl;

    // ========================================================
    // VIRTUAL INHERITANCE SUMMARY
    // ========================================================
    
    cout << "--- VIRTUAL INHERITANCE CONCEPTS ---" << endl << endl;
    
    cout << "Diamond Problem:" << endl;
    cout << "─────────────────────────────────────────" << endl;
    cout << "     A           Without virtual:" << endl;
    cout << "    / \\          A is duplicated" << endl;
    cout << "   B   C         B has one A, C has one A" << endl;
    cout << "    \\ /          D has TWO copies of A!" << endl;
    cout << "     D" << endl;
    
    cout << "\nVirtual Inheritance Solution:" << endl;
    cout << "─────────────────────────────────────────" << endl;
    cout << "class B : virtual public A { };" << endl;
    cout << "class C : virtual public A { };" << endl;
    cout << "class D : public B, public C { };" << endl;
    cout << "// D has only ONE copy of A" << endl;
    
    cout << "\nKey Rules:" << endl;
    cout << "─────────────────────────────────────────" << endl;
    cout << "1. Use 'virtual' in intermediate classes" << endl;
    cout << "2. Most-derived class initializes virtual base" << endl;
    cout << "3. Virtual base constructed before non-virtual" << endl;
    cout << "4. Virtual base destroyed after non-virtual" << endl;
    cout << "5. Adds small runtime overhead (vptr)" << endl;
    
    cout << "\nWhen to Use:" << endl;
    cout << "─────────────────────────────────────────" << endl;
    cout << "• Diamond inheritance pattern" << endl;
    cout << "• Shared interface hierarchies" << endl;
    cout << "• iostream-like designs" << endl;
    cout << "• GUI widget frameworks" << endl;
    
    cout << endl;

    cout << "============================================" << endl;
    cout << "VIRTUAL INHERITANCE COMPLETE!" << endl;
    cout << "============================================" << endl;

    return 0;
}

// ============================================================
// EXERCISES:
// ============================================================
/*
 * 1. Create a device hierarchy:
 *    - Base: Device (power, status)
 *    - Virtual: NetworkDevice, StorageDevice
 *    - Derived: NetworkAttachedStorage (both)
 *    - Demonstrate single Device instance
 * 
 * 2. Create an employee system:
 *    - Base: Person (name, ID)
 *    - Virtual: Worker, Student  
 *    - Derived: WorkingStudent (both)
 *    - Show proper initialization order
 * 
 * 3. Create a stream-like hierarchy:
 *    - Base: StreamBase
 *    - Virtual: InputStream, OutputStream
 *    - Derived: BidirectionalStream
 *    - Implement read/write methods
 * 
 * 4. Debug exercise: Given code with diamond problem,
 *    add virtual inheritance and fix initialization.
 */
Virtual Inheritance - C++ Tutorial | DeepML