cpp
virtual inheritance
01_virtual_inheritance.cpp⚙️cpp
/**
* ============================================================
* 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.
*/