Virtual Inheritance
Part of the Object Oriented Programming section of Coddy's C++ journey — lesson 54 of 104.
The diamond problem occurs when a class inherits from two classes that share a common base class. Without special handling, the derived class ends up with two copies of the common base, causing ambiguity and wasted memory.
class Animal {
public:
int age;
};
class Mammal : public Animal {};
class Bird : public Animal {};
class Bat : public Mammal, public Bird {};
Bat b;
b.age = 5; // Error: ambiguous - which 'age'?The Bat class contains two separate Animal subobjects: one through Mammal and one through Bird. This creates the diamond-shaped inheritance diagram that gives the problem its name.
Virtual inheritance solves this by ensuring only one copy of the common base exists. Add the virtual keyword when inheriting from the shared base:
class Animal {
public:
int age;
Animal(int a = 0) : age(a) {}
};
class Mammal : virtual public Animal {
public:
Mammal(int a = 0) : Animal(a) {}
};
class Bird : virtual public Animal {
public:
Bird(int a = 0) : Animal(a) {}
};
class Bat : public Mammal, public Bird {
public:
Bat(int a) : Animal(a), Mammal(a), Bird(a) {}
};
Bat b(5);
b.age = 10; // Works! Only one 'age' existsNotice that Bat must directly initialize Animal in its constructor. With virtual inheritance, the most derived class is responsible for constructing the virtual base, regardless of the intermediate classes.
Challenge
EasyLet's build a worker management system that demonstrates how virtual inheritance solves the diamond problem. You'll create a hierarchy where a TeamLead inherits from both Developer and Manager, which both share a common Employee base class.
You'll organize your code across four files:
Employee.h: Define the common base classEmployeewith:- A protected
std::string nameandint id - A constructor that takes both values and prints:
Employee [<name>] hired with ID <id> - A public
getInfo()method that prints:Employee: <name> (ID: <id>) - A virtual destructor that prints:
Employee [<name>] record closed
- A protected
Developer.h: Define aDeveloperclass that uses virtual public inheritance fromEmployee:- A protected
std::string languagemember - A constructor that takes name, id, and language — passes name and id to
Employee, stores the language, and prints:Developer [<name>] specializes in <language> - A public
code()method that prints:<name> is coding in <language> - A destructor that prints:
Developer [<name>] signed off
- A protected
Manager.h: Define aManagerclass that uses virtual public inheritance fromEmployee:- A protected
int teamSizemember - A constructor that takes name, id, and team size — passes name and id to
Employee, stores the team size, and prints:Manager [<name>] leads a team of <teamSize> - A public
manage()method that prints:<name> is managing <teamSize> people - A destructor that prints:
Manager [<name>] stepped down
- A protected
main.cpp: Read four inputs (each on a separate line):- Name (string)
- Employee ID (integer)
- Programming language (string)
- Team size (integer)
Define a
TeamLeadclass that publicly inherits from bothDeveloperandManager:- A constructor that takes all four parameters and must directly initialize
Employee(the virtual base), thenDeveloperandManager - The constructor should print:
TeamLead [<name>] ready to lead and code! - A
showRole()method that callsgetInfo(),code(), andmanage()in that order - A destructor that prints:
TeamLead [<name>] promoted out
Create a
TeamLeadobject inside a block scope, callshowRole(), then let it go out of scope. After the block, print:Organization restructured!
For example, with inputs Alice, 101, C++, and 5:
Employee [Alice] hired with ID 101
Developer [Alice] specializes in C++
Manager [Alice] leads a team of 5
TeamLead [Alice] ready to lead and code!
Employee: Alice (ID: 101)
Alice is coding in C++
Alice is managing 5 people
TeamLead [Alice] promoted out
Manager [Alice] stepped down
Developer [Alice] signed off
Employee [Alice] record closed
Organization restructured!Notice how there's only one Employee constructor call and one Employee destructor call — virtual inheritance ensures only one copy of the shared base exists. The TeamLead must directly initialize Employee because with virtual inheritance, the most derived class is responsible for constructing the virtual base.
Cheat sheet
The diamond problem occurs when a class inherits from two classes that share a common base class, resulting in two copies of the base class and causing ambiguity.
class Animal {
public:
int age;
};
class Mammal : public Animal {};
class Bird : public Animal {};
class Bat : public Mammal, public Bird {};
Bat b;
b.age = 5; // Error: ambiguous - which 'age'?Virtual inheritance solves the diamond problem by ensuring only one copy of the common base class exists. Use the virtual keyword when inheriting:
class Animal {
public:
int age;
Animal(int a = 0) : age(a) {}
};
class Mammal : virtual public Animal {
public:
Mammal(int a = 0) : Animal(a) {}
};
class Bird : virtual public Animal {
public:
Bird(int a = 0) : Animal(a) {}
};
class Bat : public Mammal, public Bird {
public:
Bat(int a) : Animal(a), Mammal(a), Bird(a) {}
};
Bat b(5);
b.age = 10; // Works! Only one 'age' existsWith virtual inheritance, the most derived class must directly initialize the virtual base class in its constructor, regardless of intermediate classes.
Try it yourself
#include <iostream>
#include <string>
#include "Developer.h"
#include "Manager.h"
using namespace std;
// TODO: Define TeamLead class that publicly inherits from both Developer and Manager
// Remember: With virtual inheritance, TeamLead must directly initialize Employee (the virtual base)
class TeamLead : public Developer, public Manager {
public:
// TODO: Implement constructor that takes name, id, language, and teamSize
// Must initialize: Employee first (virtual base), then Developer, then Manager
// Should print: TeamLead [<name>] ready to lead and code!
TeamLead(const std::string& name, int id, const std::string& language, int teamSize)
: Employee(name, id),
Developer(name, id, language),
Manager(name, id, teamSize) {
// TODO: Print constructor message
}
// TODO: Implement showRole() method
// Should call getInfo(), code(), and manage() in that order
void showRole() {
// TODO: Call the three methods
}
// TODO: Implement destructor
// Should print: TeamLead [<name>] promoted out
~TeamLead() {
// TODO: Print destructor message
}
};
int main() {
// Read inputs
string name;
int id;
string language;
int teamSize;
getline(cin, name);
cin >> id;
cin.ignore();
getline(cin, language);
cin >> teamSize;
// TODO: Create a TeamLead object inside a block scope
// Call showRole(), then let it go out of scope
{
// TODO: Create TeamLead and call showRole()
}
// Print final message after the block
cout << "Organization restructured!" << endl;
return 0;
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesC++ Build & CompilationHeader Files & Source FilesNamespaces & ScopeIntroduction to OOP in C++Classes vs ObjectsThe 'this' PointerMethods (Member Functions)Attributes (Data Members)Ctors & Dtors BasicsRecap - Simple Calculator4Class Properties
Instance vs Static MembersGetters and SettersConst Member FunctionsMutable KeywordStatic Methods and VariablesFriend Functions & ClassesRecap - Bank Account Manager7Inheritance
Basic InheritanceInheritance Access LevelsCtor & Dtor Call OrderMethod OverridingVirtual Functions & VTableMultiple InheritanceVirtual InheritanceRecap - Employee Hierarchy2Memory Management
Stack vs Heap MemoryPointers and ReferencesDynamic Memory (new/delete)Smart Pointers in C++RAII in C++Recap - Dynamic Array Manager5Encapsulation
Access Specifiers in C++Access Specifiers In DepthInformation HidingStruct vs ClassNested & Inner ClassesRecap - Student Records System8Polymorphism
Compile vs Runtime PolymorphFunction OverloadingVirtual Functions RevisitedPure Virtual FunctionsAbstract ClassesInterface Design in C++Dynamic Casting & RTTIRecap - Shape Calculator3Constructors & Destructors
Default ConstructorParameterized ConstructorCopy ConstructorMove ConstructorConstructor Init ListsDelegating ConstructorsDestructor Deep DiveRule of Three / Five / ZeroRecap - String Class6Operator Overloading
Intro to Operator OverloadArithmetic Operator OverloadComparison Operator OverloadStream OperatorsAssignment Operator Overload[] and () Operator OverloadType Conversion OperatorsRecap - Matrix Class9Templates
Function TemplatesClass TemplatesTemplate SpecializationVariadic TemplatesSFINAE & Type Traits BasicsRecap - Generic Container