C++ Inheritance: Complete Guide with Examples
Master C++ inheritance: single, multiple, multilevel, hierarchical inheritance. Learn access specifiers, polymorphism, virtual functions, and best practices for OOP.
Single Inheritance
One Base Class
Multiple Inheritance
Multiple Base Classes
Multilevel
Chain of Inheritance
Hierarchical
Multiple Derived Classes
Introduction to Inheritance
Inheritance is a fundamental Object-Oriented Programming (OOP) concept that allows a class to acquire properties and behaviors of another class. It promotes code reusability and establishes relationships between classes.
Why Use Inheritance?
- Code reusability and reduction
- Establishes "is-a" relationships
- Supports polymorphism
- Creates hierarchical classifications
- Extends existing functionality
- Improves code organization
Inheritance Components
- Base Class: Parent class being inherited from
- Derived Class: Child class that inherits
- Access Specifiers: public, private, protected
- Member Functions: Inherited methods
- Data Members: Inherited attributes
Inheritance Relationship Flow
C++ Inheritance Types
The following table compares all inheritance types in C++ with their syntax, use cases, and characteristics:
| Type | Syntax | When to Use | Characteristics |
|---|---|---|---|
| Single Inheritance | class Derived : public Base { }; | Simple "is-a" relationships, basic reuse | One base class, one derived class |
| Multiple Inheritance | class Derived : public Base1, public Base2 { }; | Combine features from multiple classes | Multiple base classes, one derived class |
| Multilevel Inheritance | class A {}; class B: public A {}; class C: public B {}; | Hierarchical systems, layered abstractions | Chain of inheritance (grandparent-parent-child) |
| Hierarchical Inheritance | class Base {}; class D1: public Base {}; class D2: public Base {}; | Multiple specialized classes from one base | One base class, multiple derived classes |
| Hybrid Inheritance | Combination of multiple types | Complex class hierarchies, special cases | Multiple + hierarchical, uses virtual inheritance |
| Virtual Inheritance | class Derived : virtual public Base { }; | Solve diamond problem in multiple inheritance | Prevents duplicate base class instances |
1. Access Specifiers in Inheritance
Access specifiers determine how base class members are accessible in derived classes. C++ provides three access specifiers: public, protected, and private.
Access Specifier Syntax
// Base class
class Base {
public:
int publicVar;
protected:
int protectedVar;
private:
int privateVar;
};
// Derived class with different access modes
class Derived : public Base {
// public inheritance:
// publicVar remains public
// protectedVar remains protected
// privateVar is inaccessible
};
class Derived : protected Base {
// protected inheritance:
// publicVar becomes protected
// protectedVar remains protected
// privateVar is inaccessible
};
class Derived : private Base {
// private inheritance:
// publicVar becomes private
// protectedVar becomes private
// privateVar is inaccessible
}
#include <iostream>
using namespace std;
// Base class demonstrating different access levels
class Animal {
public:
string name;
void speak() {
cout << name << " makes a sound." << endl;
}
protected:
string species;
void setSpecies(string s) {
species = s;
}
private:
int age;
void setAge(int a) {
age = a;
}
};
// Public Inheritance
class Dog : public Animal {
public:
Dog(string n) {
name = n; // Accessible (public in base)
setSpecies("Dog"); // Accessible (protected in base)
// age = 5; // ERROR: private in base
// setAge(5); // ERROR: private in base
}
void bark() {
cout << name << " barks: Woof! Woof!" << endl;
}
};
// Protected Inheritance
class Cat : protected Animal {
public:
Cat(string n) {
name = n; // Accessible (now protected)
setSpecies("Cat"); // Accessible (protected)
// age = 3; // ERROR: private in base
}
void meow() {
cout << name << " meows: Meow!" << endl;
}
// Provide public interface to access base methods
void makeSound() {
speak(); // Can access because it's now protected
}
};
// Private Inheritance
class Bird : private Animal {
public:
Bird(string n) {
name = n; // Accessible (now private)
setSpecies("Bird"); // Accessible (now private)
}
void chirp() {
cout << name << " chirps: Tweet! Tweet!" << endl;
}
// Provide public interface
void makeAnimalSound() {
speak(); // Can access because it's now private
}
};
int main() {
cout << "=== Public Inheritance Example ===" << endl;
Dog myDog("Buddy");
myDog.speak(); // Accessible (public inheritance)
myDog.bark();
// myDog.species = "Canine"; // ERROR: protected
cout << "\n=== Protected Inheritance Example ===" << endl;
Cat myCat("Whiskers");
myCat.meow();
myCat.makeSound(); // Uses provided interface
// myCat.speak(); // ERROR: speak() is now protected
cout << "\n=== Private Inheritance Example ===" << endl;
Bird myBird("Tweety");
myBird.chirp();
myBird.makeAnimalSound(); // Uses provided interface
// myBird.speak(); // ERROR: speak() is now private
return 0;
}
- public: Accessible everywhere
- protected: Accessible in derived classes
- private: Accessible only in own class
- Choose
publicinheritance for "is-a" relationships - Use
privateinheritance for "implemented-in-terms-of"
- Forgetting access specifiers
- Using private inheritance incorrectly
- Trying to access private base members
- Not providing interfaces for private/protected inheritance
- Confusing "has-a" with "is-a" relationships
2. Single Inheritance
Single inheritance is the simplest form where a derived class inherits from only one base class.
Basic Syntax
class Base {
// Base class members
};
class Derived : access-specifier Base {
// Derived class members
};
#include <iostream>
#include <string>
using namespace std;
// Base class
class Vehicle {
protected:
string brand;
int year;
public:
Vehicle(string b, int y) : brand(b), year(y) {
cout << "Vehicle constructor called." << endl;
}
void displayInfo() {
cout << "Brand: " << brand << ", Year: " << year;
}
void start() {
cout << brand << " is starting..." << endl;
}
~Vehicle() {
cout << "Vehicle destructor called." << endl;
}
};
// Derived class using single inheritance
class Car : public Vehicle {
private:
int doors;
string fuelType;
public:
// Constructor with initialization list
Car(string b, int y, int d, string f)
: Vehicle(b, y), doors(d), fuelType(f) {
cout << "Car constructor called." << endl;
}
// Override base class function
void displayInfo() {
Vehicle::displayInfo(); // Call base class method
cout << ", Doors: " << doors
<< ", Fuel: " << fuelType << endl;
}
// New functionality specific to Car
void honk() {
cout << brand << " honks: Beep! Beep!" << endl;
}
// Additional methods
void refuel() {
cout << "Refueling " << brand << " with " << fuelType << endl;
}
~Car() {
cout << "Car destructor called." << endl;
}
};
// Another derived class
class Motorcycle : public Vehicle {
private:
bool hasHelmet;
public:
Motorcycle(string b, int y, bool helmet)
: Vehicle(b, y), hasHelmet(helmet) {
cout << "Motorcycle constructor called." << endl;
}
void displayInfo() {
Vehicle::displayInfo();
cout << ", Has helmet: " << (hasHelmet ? "Yes" : "No") << endl;
}
void wheelie() {
cout << brand << " is doing a wheelie!" << endl;
}
};
int main() {
cout << "=== Single Inheritance Demo ===" << endl << endl;
// Create Car object
cout << "Creating Car object:" << endl;
Car myCar("Toyota", 2022, 4, "Gasoline");
myCar.displayInfo();
myCar.start();
myCar.honk();
myCar.refuel();
cout << endl;
// Create Motorcycle object
cout << "Creating Motorcycle object:" << endl;
Motorcycle myBike("Harley", 2021, true);
myBike.displayInfo();
myBike.start();
myBike.wheelie();
cout << endl;
// Using base class pointer with derived objects
cout << "Using base class pointers:" << endl;
Vehicle* vehicle1 = &myCar;
Vehicle* vehicle2 = &myBike;
vehicle1->start();
vehicle2->start();
// vehicle1->honk(); // ERROR: Vehicle doesn't have honk()
return 0;
}
Key Points: Single Inheritance
- Most common and straightforward inheritance type
- Establishes clear "is-a" relationship
- Derived class can override base class methods
- Constructors called from base to derived
- Destructors called from derived to base
3. Multiple Inheritance
Multiple inheritance allows a class to inherit from more than one base class, combining their features.
Basic Syntax
class Base1 {
// First base class
};
class Base2 {
// Second base class
};
class Derived : public Base1, public Base2 {
// Inherits from both Base1 and Base2
};
#include <iostream>
#include <string>
using namespace std;
// First base class
class Printer {
protected:
string brand;
int dpi;
public:
Printer(string b, int d) : brand(b), dpi(d) {}
void printInfo() {
cout << "Printer: " << brand << " (" << dpi << " DPI)" << endl;
}
virtual void printDocument(string doc) {
cout << "Printing document: " << doc << endl;
}
};
// Second base class
class Scanner {
protected:
string scanType;
int resolution;
public:
Scanner(string t, int r) : scanType(t), resolution(r) {}
void scanInfo() {
cout << "Scanner type: " << scanType
<< " (" << resolution << " DPI)" << endl;
}
virtual void scanDocument() {
cout << "Scanning document..." << endl;
}
};
// Third base class
class Fax {
protected:
string faxNumber;
public:
Fax(string num) : faxNumber(num) {}
void faxInfo() {
cout << "Fax number: " << faxNumber << endl;
}
virtual void sendFax(string doc) {
cout << "Sending fax: " << doc
<< " to " << faxNumber << endl;
}
};
// Derived class with multiple inheritance
class MultifunctionPrinter : public Printer, public Scanner, public Fax {
private:
bool isColor;
public:
MultifunctionPrinter(string b, int dpi, string scanT,
int res, string faxNum, bool color)
: Printer(b, dpi), Scanner(scanT, res),
Fax(faxNum), isColor(color) {}
// Override methods
void printDocument(string doc) override {
cout << "MFP printing: " << doc;
if (isColor) cout << " in COLOR";
cout << endl;
}
void scanDocument() override {
cout << "MFP scanning at " << resolution << " DPI" << endl;
}
// New functionality
void copyDocument(string doc) {
cout << "Copying document: " << doc << endl;
scanDocument(); // Simulate scanning
printDocument(doc); // Simulate printing
}
void displayAllInfo() {
printInfo();
scanInfo();
faxInfo();
cout << "Color capable: " << (isColor ? "Yes" : "No") << endl;
}
};
// Another example: Student and Employee
class Student {
protected:
string studentId;
float gpa;
public:
Student(string id, float g) : studentId(id), gpa(g) {}
void study() {
cout << "Student " << studentId << " is studying." << endl;
}
void displayStudentInfo() {
cout << "Student ID: " << studentId
<< ", GPA: " << gpa << endl;
}
};
class Employee {
protected:
string employeeId;
float salary;
public:
Employee(string id, float s) : employeeId(id), salary(s) {}
void work() {
cout << "Employee " << employeeId << " is working." << endl;
}
void displayEmployeeInfo() {
cout << "Employee ID: " << employeeId
<< ", Salary: $" << salary << endl;
}
};
// StudentEmployee inherits from both Student and Employee
class StudentEmployee : public Student, public Employee {
private:
int hoursPerWeek;
public:
StudentEmployee(string sid, float g, string eid,
float s, int hours)
: Student(sid, g), Employee(eid, s),
hoursPerWeek(hours) {}
void displayInfo() {
displayStudentInfo();
displayEmployeeInfo();
cout << "Hours per week: " << hoursPerWeek << endl;
}
void manageSchedule() {
cout << "Managing schedule as student and employee." << endl;
study();
work();
}
};
int main() {
cout << "=== Multiple Inheritance Demo ===" << endl << endl;
// Multifunction printer example
cout << "1. Multifunction Printer:" << endl;
MultifunctionPrinter mfp("HP", 1200, "Flatbed",
600, "555-1234", true);
mfp.displayAllInfo();
mfp.printDocument("Report.pdf");
mfp.scanDocument();
mfp.sendFax("Contract.docx");
mfp.copyDocument("Notes.txt");
cout << endl;
// StudentEmployee example
cout << "2. Student Employee:" << endl;
StudentEmployee intern("S12345", 3.8, "E9876",
2000.0, 20);
intern.displayInfo();
intern.manageSchedule();
return 0;
}
Advantages
- Combine features from multiple classes
- Greater code reuse
- Flexible class design
- Model complex real-world relationships
Disadvantages
- Diamond problem (ambiguous inheritance)
- Increased complexity
- Constructor order complications
- Harder to maintain
4. Multilevel Inheritance
Multilevel inheritance involves a chain of inheritance where a class is derived from another derived class.
Basic Syntax
class Grandparent {
// Base class
};
class Parent : public Grandparent {
// Intermediate class
};
class Child : public Parent {
// Most derived class
};
#include <iostream>
#include <string>
using namespace std;
// Level 1: Base class
class Animal {
protected:
string species;
int age;
public:
Animal(string s, int a) : species(s), age(a) {
cout << "Animal constructor: " << species << endl;
}
void breathe() {
cout << species << " is breathing." << endl;
}
void eat(string food) {
cout << species << " is eating " << food << "." << endl;
}
void displayInfo() {
cout << "Species: " << species
<< ", Age: " << age << " years" << endl;
}
virtual ~Animal() {
cout << "Animal destructor: " << species << endl;
}
};
// Level 2: Intermediate class
class Mammal : public Animal {
protected:
bool hasFur;
string furColor;
public:
Mammal(string s, int a, bool fur, string color)
: Animal(s, a), hasFur(fur), furColor(color) {
cout << "Mammal constructor: " << species << endl;
}
void nurseYoung() {
cout << species << " is nursing its young." << endl;
}
void displayInfo() {
Animal::displayInfo();
cout << "Has fur: " << (hasFur ? "Yes" : "No");
if (hasFur) cout << ", Color: " << furColor;
cout << endl;
}
~Mammal() {
cout << "Mammal destructor: " << species << endl;
}
};
// Level 3: Further derived class
class Dog : public Mammal {
private:
string breed;
string barkSound;
public:
Dog(string s, int a, bool fur, string color,
string b, string sound)
: Mammal(s, a, fur, color), breed(b), barkSound(sound) {
cout << "Dog constructor: " << species << endl;
}
void bark() {
cout << species << " barks: " << barkSound << endl;
}
void fetch(string item) {
cout << species << " is fetching " << item << "." << endl;
}
void displayInfo() {
Mammal::displayInfo();
cout << "Breed: " << breed << endl;
}
// Override eat method
void eat(string food) override {
if (food == "chocolate") {
cout << species << " cannot eat chocolate! It's toxic!" << endl;
} else {
Animal::eat(food);
}
}
~Dog() {
cout << "Dog destructor: " << species << endl;
}
};
// Another level: Specialized dog
class ServiceDog : public Dog {
private:
string serviceType;
string handlerName;
public:
ServiceDog(string s, int a, bool fur, string color,
string b, string sound, string service, string handler)
: Dog(s, a, fur, color, b, sound),
serviceType(service), handlerName(handler) {
cout << "ServiceDog constructor: " << species << endl;
}
void performService() {
cout << species << " is performing "
<< serviceType << " service." << endl;
}
void displayInfo() {
Dog::displayInfo();
cout << "Service type: " << serviceType
<< ", Handler: " << handlerName << endl;
}
~ServiceDog() {
cout << "ServiceDog destructor: " << species << endl;
}
};
// Different branch of multilevel inheritance
class Bird : public Animal {
protected:
double wingspan;
bool canFly;
public:
Bird(string s, int a, double ws, bool fly)
: Animal(s, a), wingspan(ws), canFly(fly) {
cout << "Bird constructor: " << species << endl;
}
void fly() {
if (canFly) {
cout << species << " is flying with "
<< wingspan << "m wingspan." << endl;
} else {
cout << species << " cannot fly." << endl;
}
}
void displayInfo() {
Animal::displayInfo();
cout << "Wingspan: " << wingspan
<< "m, Can fly: " << (canFly ? "Yes" : "No") << endl;
}
};
class Parrot : public Bird {
private:
string vocabulary;
public:
Parrot(string s, int a, double ws, bool fly, string vocab)
: Bird(s, a, ws, fly), vocabulary(vocab) {
cout << "Parrot constructor: " << species << endl;
}
void talk() {
cout << species << " says: \"" << vocabulary << "\"" << endl;
}
};
int main() {
cout << "=== Multilevel Inheritance Demo ===" << endl << endl;
cout << "1. Dog (Animal -> Mammal -> Dog):" << endl;
Dog myDog("Canine", 5, true, "Brown",
"Golden Retriever", "Woof! Woof!");
myDog.displayInfo();
myDog.breathe();
myDog.eat("dog food");
myDog.eat("chocolate"); // Overridden method
myDog.nurseYoung(); // From Mammal
myDog.bark();
myDog.fetch("ball");
cout << endl;
cout << "2. ServiceDog (Animal -> Mammal -> Dog -> ServiceDog):" << endl;
ServiceDog serviceDog("Canine", 4, true, "Black",
"Labrador", "Woof!",
"guide", "John");
serviceDog.displayInfo();
serviceDog.performService();
cout << endl;
cout << "3. Parrot (Animal -> Bird -> Parrot):" << endl;
Parrot myParrot("Parrot", 2, 0.5, true,
"Hello! Pretty bird!");
myParrot.displayInfo();
myParrot.fly();
myParrot.talk();
cout << endl;
cout << "Destructors will be called in reverse order..." << endl;
return 0;
}
- Constructors: Base → Intermediate → Derived (top to bottom)
- Destructors: Derived → Intermediate → Base (bottom to top)
- Each constructor initializes its own members first
- Virtual destructors ensure proper cleanup
- Use initialization lists for efficiency
5. Hierarchical Inheritance
Hierarchical inheritance involves multiple derived classes inheriting from a single base class.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// Base class: Shape
class Shape {
protected:
string name;
string color;
public:
Shape(string n, string c) : name(n), color(c) {}
virtual double area() = 0; // Pure virtual function
virtual double perimeter() = 0; // Pure virtual function
void displayInfo() {
cout << name << " (" << color << ")" << endl;
cout << "Area: " << area() << ", Perimeter: "
<< perimeter() << endl;
}
virtual void draw() {
cout << "Drawing " << name << " in " << color << " color." << endl;
}
virtual ~Shape() {
cout << "Shape destructor: " << name << endl;
}
};
// Derived class 1: Circle
class Circle : public Shape {
private:
double radius;
public:
Circle(string n, string c, double r)
: Shape(n, c), radius(r) {}
double area() override {
return 3.14159 * radius * radius;
}
double perimeter() override {
return 2 * 3.14159 * radius;
}
void draw() override {
Shape::draw();
cout << " Drawing circle with radius " << radius << endl;
}
// Circle-specific method
double diameter() {
return 2 * radius;
}
};
// Derived class 2: Rectangle
class Rectangle : public Shape {
private:
double length;
double width;
public:
Rectangle(string n, string c, double l, double w)
: Shape(n, c), length(l), width(w) {}
double area() override {
return length * width;
}
double perimeter() override {
return 2 * (length + width);
}
void draw() override {
Shape::draw();
cout << " Drawing rectangle " << length
<< " x " << width << endl;
}
// Rectangle-specific method
bool isSquare() {
return length == width;
}
};
// Derived class 3: Triangle
class Triangle : public Shape {
private:
double base;
double height;
double side1, side2, side3;
public:
Triangle(string n, string c, double b, double h,
double s1, double s2, double s3)
: Shape(n, c), base(b), height(h),
side1(s1), side2(s2), side3(s3) {}
double area() override {
return 0.5 * base * height;
}
double perimeter() override {
return side1 + side2 + side3;
}
void draw() override {
Shape::draw();
cout << " Drawing triangle with sides "
<< side1 << ", " << side2 << ", " << side3 << endl;
}
// Triangle-specific method
string triangleType() {
if (side1 == side2 && side2 == side3) return "Equilateral";
if (side1 == side2 || side2 == side3 || side1 == side3) return "Isosceles";
return "Scalene";
}
};
// Another hierarchical example: Employees
class Employee {
protected:
string name;
int id;
double salary;
public:
Employee(string n, int i, double s)
: name(n), id(i), salary(s) {}
virtual void work() {
cout << name << " is working." << endl;
}
virtual void displayInfo() {
cout << "ID: " << id << ", Name: " << name
<< ", Salary: $" << salary << endl;
}
virtual double calculateBonus() {
return 0.0; // Base implementation
}
};
class Manager : public Employee {
private:
string department;
int teamSize;
public:
Manager(string n, int i, double s, string dept, int team)
: Employee(n, i, s), department(dept), teamSize(team) {}
void work() override {
cout << name << " is managing " << department
<< " department." << endl;
}
void displayInfo() override {
Employee::displayInfo();
cout << " Department: " << department
<< ", Team size: " << teamSize << endl;
}
double calculateBonus() override {
return salary * 0.20 + teamSize * 100;
}
void conductMeeting() {
cout << name << " is conducting a team meeting." << endl;
}
};
class Developer : public Employee {
private:
string programmingLanguage;
int yearsExperience;
public:
Developer(string n, int i, double s, string lang, int exp)
: Employee(n, i, s), programmingLanguage(lang),
yearsExperience(exp) {}
void work() override {
cout << name << " is coding in "
<< programmingLanguage << "." << endl;
}
void displayInfo() override {
Employee::displayInfo();
cout << " Language: " << programmingLanguage
<< ", Experience: " << yearsExperience
<< " years" << endl;
}
double calculateBonus() override {
return salary * 0.15 + yearsExperience * 500;
}
void debugCode() {
cout << name << " is debugging code." << endl;
}
};
class SalesPerson : public Employee {
private:
double commissionRate;
double salesAmount;
public:
SalesPerson(string n, int i, double s, double rate, double sales)
: Employee(n, i, s), commissionRate(rate),
salesAmount(sales) {}
void work() override {
cout << name << " is making sales calls." << endl;
}
void displayInfo() override {
Employee::displayInfo();
cout << " Commission: " << (commissionRate * 100)
<< "%, Sales: $" << salesAmount << endl;
}
double calculateBonus() override {
return salesAmount * commissionRate;
}
void makeSale(double amount) {
salesAmount += amount;
cout << name << " made a sale of $" << amount << "!" << endl;
}
};
int main() {
cout << "=== Hierarchical Inheritance Demo ===" << endl << endl;
cout << "1. Shapes (Base: Shape):" << endl;
Circle circle("Circle", "Red", 5.0);
Rectangle rectangle("Rectangle", "Blue", 4.0, 6.0);
Triangle triangle("Triangle", "Green", 3.0, 4.0, 3.0, 4.0, 5.0);
circle.displayInfo();
circle.draw();
cout << "Diameter: " << circle.diameter() << endl << endl;
rectangle.displayInfo();
rectangle.draw();
cout << "Is square? " << (rectangle.isSquare() ? "Yes" : "No")
<< endl << endl;
triangle.displayInfo();
triangle.draw();
cout << "Triangle type: " << triangle.triangleType()
<< endl << endl;
cout << "2. Employees (Base: Employee):" << endl;
Manager manager("Alice", 101, 80000, "Engineering", 10);
Developer developer("Bob", 102, 70000, "C++", 5);
SalesPerson salesperson("Charlie", 103, 50000, 0.10, 100000);
manager.displayInfo();
manager.work();
manager.conductMeeting();
cout << "Bonus: $" << manager.calculateBonus() << endl << endl;
developer.displayInfo();
developer.work();
developer.debugCode();
cout << "Bonus: $" << developer.calculateBonus() << endl << endl;
salesperson.displayInfo();
salesperson.work();
salesperson.makeSale(5000);
cout << "Bonus: $" << salesperson.calculateBonus() << endl;
return 0;
}
- When you have a general category with specific subcategories
- When multiple classes share common functionality
- For implementing polymorphism through base class pointers
- When creating class libraries or frameworks
- For database entity relationships
6. Virtual Functions and Polymorphism
Polymorphism allows objects of different classes to be treated as objects of a common base class. Virtual functions enable runtime polymorphism.
Virtual Function
Function that can be overridden in derived classes.
class Base {
public:
virtual void show() {
cout << "Base show";
}
};
class Derived : public Base {
public:
void show() override {
cout << "Derived show";
}
};
Pure Virtual Function
Makes class abstract (cannot be instantiated).
class Abstract {
public:
virtual void pure() = 0;
};
class Concrete : public Abstract {
public:
void pure() override {
// Implementation
}
};
Virtual Destructor
Ensures proper cleanup of derived objects.
class Base {
public:
virtual ~Base() {
// Cleanup
}
};
class Derived : public Base {
public:
~Derived() override {
// Derived cleanup
}
};
#include <iostream>
#include <vector>
#include <memory>
using namespace std;
// Abstract base class with pure virtual function
class Animal {
protected:
string name;
public:
Animal(string n) : name(n) {}
// Pure virtual function - makes class abstract
virtual void makeSound() = 0;
// Virtual function with implementation
virtual void move() {
cout << name << " is moving." << endl;
}
// Non-virtual function
void eat(string food) {
cout << name << " is eating " << food << "." << endl;
}
// Virtual destructor
virtual ~Animal() {
cout << "Animal destructor: " << name << endl;
}
};
// Derived classes implementing pure virtual function
class Dog : public Animal {
public:
Dog(string n) : Animal(n) {}
void makeSound() override {
cout << name << " says: Woof! Woof!" << endl;
}
void move() override {
cout << name << " is running on four legs." << endl;
}
// Dog-specific method
void fetch() {
cout << name << " is fetching the ball." << endl;
}
~Dog() override {
cout << "Dog destructor: " << name << endl;
}
};
class Cat : public Animal {
public:
Cat(string n) : Animal(n) {}
void makeSound() override {
cout << name << " says: Meow! Meow!" << endl;
}
void move() override {
cout << name << " is walking gracefully." << endl;
}
// Cat-specific method
void climb() {
cout << name << " is climbing the tree." << endl;
}
~Cat() override {
cout << "Cat destructor: " << name << endl;
}
};
class Bird : public Animal {
public:
Bird(string n) : Animal(n) {}
void makeSound() override {
cout << name << " says: Tweet! Tweet!" << endl;
}
void move() override {
cout << name << " is flying in the sky." << endl;
}
// Bird-specific method
void buildNest() {
cout << name << " is building a nest." << endl;
}
~Bird() override {
cout << "Bird destructor: " << name << endl;
}
};
// Function demonstrating polymorphism
void animalShow(Animal* animal) {
animal->makeSound();
animal->move();
animal->eat("food");
cout << endl;
}
int main() {
cout << "=== Polymorphism Demo ===" << endl << endl;
// Create objects of derived classes
Dog dog("Buddy");
Cat cat("Whiskers");
Bird bird("Tweety");
// Using base class pointers with derived objects
Animal* animal1 = &dog;
Animal* animal2 = &cat;
Animal* animal3 = &bird;
cout << "1. Using base class pointers:" << endl;
animal1->makeSound(); // Calls Dog::makeSound()
animal2->makeSound(); // Calls Cat::makeSound()
animal3->makeSound(); // Calls Bird::makeSound()
cout << endl << "2. Function with polymorphism:" << endl;
animalShow(&dog);
animalShow(&cat);
animalShow(&bird);
cout << "3. Array/Vector of base class pointers:" << endl;
vector<Animal*> zoo;
zoo.push_back(new Dog("Rex"));
zoo.push_back(new Cat("Mittens"));
zoo.push_back(new Bird("Polly"));
zoo.push_back(new Dog("Max"));
for (Animal* animal : zoo) {
animal->makeSound();
}
cout << endl << "4. Dynamic casting to access derived methods:" << endl;
for (Animal* animal : zoo) {
if (Dog* dogPtr = dynamic_cast<Dog*>(animal)) {
dogPtr->fetch();
} else if (Cat* catPtr = dynamic_cast<Cat*>(animal)) {
catPtr->climb();
} else if (Bird* birdPtr = dynamic_cast<Bird*>(animal)) {
birdPtr->buildNest();
}
}
cout << endl << "5. Smart pointers with polymorphism:" << endl;
vector<unique_ptr<Animal>> smartZoo;
smartZoo.push_back(make_unique<Dog>("SmartRex"));
smartZoo.push_back(make_unique<Cat>("SmartMittens"));
smartZoo.push_back(make_unique<Bird>("SmartPolly"));
for (auto& animal : smartZoo) {
animal->makeSound();
}
// Cleanup for raw pointers
for (Animal* animal : zoo) {
delete animal;
}
zoo.clear();
cout << endl << "Destructors called automatically..." << endl;
return 0;
}
- Always declare destructor as virtual in base classes
- Use override keyword (C++11) for clarity
- Virtual functions have runtime overhead
- Constructors cannot be virtual
- Static functions cannot be virtual
- Use final keyword (C++11) to prevent further overriding
7. Diamond Problem and Virtual Inheritance
The diamond problem occurs in multiple inheritance when a class inherits from two classes that both inherit from the same base class. Virtual inheritance solves this.
#include <iostream>
using namespace std;
// Base class
class Animal {
protected:
string species;
int age;
public:
Animal(string s, int a) : species(s), age(a) {
cout << "Animal constructor called." << endl;
}
void breathe() {
cout << species << " is breathing." << endl;
}
virtual ~Animal() {
cout << "Animal destructor called." << endl;
}
};
// WITHOUT virtual inheritance - PROBLEM
class Mammal : public Animal {
public:
Mammal(string s, int a) : Animal(s, a) {
cout << "Mammal constructor called." << endl;
}
void nurse() {
cout << species << " is nursing young." << endl;
}
};
class WingedAnimal : public Animal {
public:
WingedAnimal(string s, int a) : Animal(s, a) {
cout << "WingedAnimal constructor called." << endl;
}
void fly() {
cout << species << " is flying." << endl;
}
};
// Bat inherits from both Mammal and WingedAnimal
// PROBLEM: Two copies of Animal base class
class BatProblem : public Mammal, public WingedAnimal {
public:
BatProblem(string s, int a)
: Mammal(s, a), WingedAnimal(s, a) {
cout << "BatProblem constructor called." << endl;
}
void display() {
// ERROR: Ambiguous - which species?
// cout << "Species: " << species << endl;
// Need to specify which base class
cout << "Mammal species: " << Mammal::species << endl;
cout << "WingedAnimal species: " << WingedAnimal::species << endl;
}
};
// WITH virtual inheritance - SOLUTION
class VirtualMammal : virtual public Animal {
public:
VirtualMammal(string s, int a) : Animal(s, a) {
cout << "VirtualMammal constructor called." << endl;
}
void nurse() {
cout << species << " is nursing young." << endl;
}
};
class VirtualWingedAnimal : virtual public Animal {
public:
VirtualWingedAnimal(string s, int a) : Animal(s, a) {
cout << "VirtualWingedAnimal constructor called." << endl;
}
void fly() {
cout << species << " is flying." << endl;
}
};
// BatSolution inherits from both virtual base classes
class BatSolution : public VirtualMammal, public VirtualWingedAnimal {
public:
// Animal constructor called directly by most derived class
BatSolution(string s, int a)
: Animal(s, a), VirtualMammal(s, a), VirtualWingedAnimal(s, a) {
cout << "BatSolution constructor called." << endl;
}
void display() {
// NO AMBIGUITY: Only one copy of Animal
cout << "Species: " << species << ", Age: " << age << endl;
}
void batBehavior() {
cout << species << " is using echolocation." << endl;
nurse(); // From VirtualMammal
fly(); // From VirtualWingedAnimal
}
};
// Another practical example: Teaching Assistant
class Person {
protected:
string name;
int id;
public:
Person(string n, int i) : name(n), id(i) {
cout << "Person constructor: " << name << endl;
}
void introduce() {
cout << "Hi, I'm " << name << " (ID: " << id << ")" << endl;
}
virtual ~Person() {
cout << "Person destructor: " << name << endl;
}
};
// Without virtual inheritance (problem)
class StudentProblem : public Person {
public:
StudentProblem(string n, int i) : Person(n, i) {
cout << "StudentProblem constructor." << endl;
}
void study() {
cout << name << " is studying." << endl;
}
};
class TeacherProblem : public Person {
public:
TeacherProblem(string n, int i) : Person(n, i) {
cout << "TeacherProblem constructor." << endl;
}
void teach() {
cout << name << " is teaching." << endl;
}
};
// TeachingAssistant inherits from both Student and Teacher
class TeachingAssistantProblem : public StudentProblem, public TeacherProblem {
public:
TeachingAssistantProblem(string n, int studentId, int teacherId)
: StudentProblem(n, studentId), TeacherProblem(n, teacherId) {
cout << "TeachingAssistantProblem constructor." << endl;
}
void work() {
// Ambiguous: which name?
// cout << name << " is working as TA." << endl;
StudentProblem::study();
TeacherProblem::teach();
}
};
// With virtual inheritance (solution)
class StudentSolution : virtual public Person {
public:
StudentSolution(string n, int i) : Person(n, i) {
cout << "StudentSolution constructor." << endl;
}
void study() {
cout << name << " is studying." << endl;
}
};
class TeacherSolution : virtual public Person {
public:
TeacherSolution(string n, int i) : Person(n, i) {
cout << "TeacherSolution constructor." << endl;
}
void teach() {
cout << name << " is teaching." << endl;
}
};
class TeachingAssistantSolution : public StudentSolution, public TeacherSolution {
public:
TeachingAssistantSolution(string n, int id)
: Person(n, id), StudentSolution(n, id), TeacherSolution(n, id) {
cout << "TeachingAssistantSolution constructor." << endl;
}
void work() {
// No ambiguity
cout << name << " is working as TA." << endl;
study();
teach();
}
};
int main() {
cout << "=== Diamond Problem and Virtual Inheritance ===" << endl << endl;
cout << "1. PROBLEM: Bat without virtual inheritance" << endl;
BatProblem batProblem("Bat", 2);
cout << "Size of BatProblem: " << sizeof(batProblem)
<< " bytes (has two Animal copies)" << endl;
batProblem.display();
// batProblem.breathe(); // ERROR: ambiguous
batProblem.Mammal::breathe();
batProblem.WingedAnimal::breathe();
cout << endl;
cout << "2. SOLUTION: Bat with virtual inheritance" << endl;
BatSolution batSolution("Bat", 3);
cout << "Size of BatSolution: " << sizeof(batSolution)
<< " bytes (has one Animal copy)" << endl;
batSolution.display();
batSolution.breathe(); // No ambiguity
batSolution.batBehavior();
cout << endl;
cout << "3. Teaching Assistant Problem" << endl;
TeachingAssistantProblem taProblem("Alice", 1001, 2001);
cout << "Size: " << sizeof(taProblem) << " bytes" << endl;
taProblem.StudentProblem::introduce(); // Need to specify
taProblem.TeacherProblem::introduce(); // Two different Persons
taProblem.work();
cout << endl;
cout << "4. Teaching Assistant Solution" << endl;
TeachingAssistantSolution taSolution("Bob", 3001);
cout << "Size: " << sizeof(taSolution) << " bytes" << endl;
taSolution.introduce(); // No ambiguity
taSolution.work();
cout << endl << "Destructors called in reverse order..." << endl;
return 0;
}
- Use virtual inheritance to solve diamond problem
- Most derived class calls virtual base constructor directly
- Virtual base constructor called before non-virtual base constructors
- Adds overhead (virtual base pointer)
- Use only when diamond inheritance is necessary
- Consider composition over multiple inheritance
8. Best Practices and Common Mistakes
Best Practices
- Use public inheritance for "is-a" relationships
- Prefer composition over multiple inheritance
- Always make destructors virtual in base classes
- Use override keyword (C++11) for clarity
- Keep inheritance hierarchies shallow (2-3 levels)
- Use abstract base classes for interfaces
- Document inheritance relationships clearly
Common Mistakes
- Forgetting virtual destructors
- Deep inheritance hierarchies
- Using multiple inheritance unnecessarily
- Confusing "has-a" with "is-a" relationships
- Not using access specifiers correctly
- Circular inheritance dependencies
- Ignoring the diamond problem
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// BAD DESIGN: Deep hierarchy, improper relationships
class BadVehicle {
// Too many responsibilities
};
class BadCar : public BadVehicle {
// ...
};
class BadSportsCar : public BadCar {
// Too deep
};
class BadConvertible : public BadSportsCar {
// Even deeper
};
// BAD: Multiple inheritance for wrong reasons
class Engine {
// ...
};
class Radio {
// ...
};
class BadCarDesign : public Engine, public Radio { // WRONG: Car has-an engine, not is-an engine
// ...
};
// GOOD DESIGN: Proper "is-a" relationships
class GoodVehicle {
protected:
string make;
string model;
int year;
public:
GoodVehicle(string m, string mdl, int y)
: make(m), model(mdl), year(y) {}
virtual void start() = 0; // Pure virtual
virtual void stop() = 0; // Pure virtual
virtual ~GoodVehicle() = default;
};
class GoodCar : public GoodVehicle {
protected:
int doors;
public:
GoodCar(string m, string mdl, int y, int d)
: GoodVehicle(m, mdl, y), doors(d) {}
void start() override {
cout << make << " " << model << " car starting." << endl;
}
void stop() override {
cout << make << " " << model << " car stopping." << endl;
}
};
class GoodMotorcycle : public GoodVehicle {
protected:
bool hasSidecar;
public:
GoodMotorcycle(string m, string mdl, int y, bool sidecar)
: GoodVehicle(m, mdl, y), hasSidecar(sidecar) {}
void start() override {
cout << make << " " << model << " motorcycle starting." << endl;
}
void stop() override {
cout << make << " " << model << " motorcycle stopping." << endl;
}
};
// GOOD: Composition over inheritance
class RadioComponent {
private:
string brand;
int maxVolume;
public:
RadioComponent(string b, int vol) : brand(b), maxVolume(vol) {}
void turnOn() {
cout << brand << " radio turned on." << endl;
}
void setStation(double frequency) {
cout << "Tuned to " << frequency << " MHz." << endl;
}
};
class EngineComponent {
private:
int horsepower;
string fuelType;
public:
EngineComponent(int hp, string fuel)
: horsepower(hp), fuelType(fuel) {}
void ignite() {
cout << "Engine (" << horsepower
<< " HP) ignited with " << fuelType << endl;
}
};
class GoodCarDesign {
private:
GoodVehicle* vehicle; // Has-a vehicle (could be any type)
EngineComponent engine;
RadioComponent radio;
public:
GoodCarDesign(GoodVehicle* v, int hp, string fuel, string radioBrand)
: vehicle(v), engine(hp, fuel), radio(radioBrand, 100) {}
void operate() {
vehicle->start();
engine.ignite();
radio.turnOn();
radio.setStation(98.5);
vehicle->stop();
}
~GoodCarDesign() {
delete vehicle;
}
};
// GOOD: Interface inheritance
class IPrintable {
public:
virtual void print() const = 0;
virtual ~IPrintable() = default;
};
class IScannable {
public:
virtual void scan() = 0;
virtual ~IScannable() = default;
};
class Document : public IPrintable {
private:
string content;
public:
Document(string c) : content(c) {}
void print() const override {
cout << "Printing document: " << content << endl;
}
};
class Photo : public IPrintable, public IScannable {
private:
string imageData;
public:
Photo(string img) : imageData(img) {}
void print() const override {
cout << "Printing photo..." << endl;
}
void scan() override {
cout << "Scanning photo..." << endl;
}
};
void printItem(const IPrintable& item) {
item.print(); // Polymorphic call
}
int main() {
cout << "=== Good Inheritance Design Examples ===" << endl << endl;
// Good inheritance hierarchy
GoodCar car("Toyota", "Camry", 2022, 4);
GoodMotorcycle bike("Harley", "Davidson", 2021, false);
car.start();
car.stop();
bike.start();
bike.stop();
cout << endl;
// Composition example
GoodCarDesign carDesign(new GoodCar("Honda", "Accord", 2023, 4),
200, "gasoline", "Sony");
carDesign.operate();
cout << endl;
// Interface inheritance
Document doc("Hello World Document");
Photo photo("Vacation.jpg");
vector<const IPrintable*> items;
items.push_back(&doc);
items.push_back(&photo);
for (const IPrintable* item : items) {
item->print();
}
// Dynamic casting to check interface
if (IScannable* scannable = dynamic_cast<IScannable*>(&photo)) {
scannable->scan();
}
return 0;
}