Search Knowledge

© 2026 LIBREUNI PROJECT

Modern C++ Programming / Object-Oriented Programming

Inheritance and Polymorphism

Inheritance: The Is-A Relationship

Inheritance models taxonomic relationships where a derived class “is a” specialized version of a base class. C++ supports single and multiple inheritance with three access modes.

class Shape {
protected:
    std::string color_;
public:
    Shape(std::string c) : color_(c) {}
    virtual double area() const = 0;  // Pure virtual
    virtual ~Shape() = default;
};

class Circle : public Shape {
    double radius_;
public:
    Circle(std::string c, double r) : Shape(c), radius_(r) {}
    
    double area() const override {
        return 3.14159 * radius_ * radius_;
    }
};

Inheritance Access Modes

DerivationBase public → DerivedBase protected → DerivedBase private → Derived
publicpublicprotectedinaccessible
protectedprotectedprotectedinaccessible
privateprivateprivateinaccessible

Public inheritance models “is-a”: Circle is-a Shape. Most common. Protected inheritance models “implemented-in-terms-of” (rare). Private inheritance models “implemented-in-terms-of” (prefer composition).

class Stack : private std::vector<int> {  // Private inheritance
public:
    void push(int x) { push_back(x); }
    int pop() { int x = back(); pop_back(); return x; }
};
// Stack is NOT a std::vector publicly

Virtual Functions and Dynamic Dispatch

Virtual functions enable runtime polymorphism: the called function depends on the dynamic type of the object, not the static type of the pointer/reference.

class Animal {
public:
    virtual void speak() const {
        std::cout << "Some sound\\n";
    }
    virtual ~Animal() = default;
};

class Dog : public Animal {
public:
    void speak() const override {  // override keyword: C++11
        std::cout << "Woof!\\n";
    }
};

void makeNoise(const Animal& a) {
    a.speak();  // Dynamic dispatch
}

Dog d;
makeNoise(d);  // Outputs: Woof!

The Virtual Table Mechanism

Virtual functions are implemented via a virtual table (vtable): a compile-time array of function pointers for each polymorphic class. Each object contains a hidden vptr pointing to its class’s vtable.

System Diagram
Dog Objectvptr →data members...Dog vtable[0]: Dog::speak()[1]: Dog::~Dog()

Cost: One pointer per object, one indirection per virtual call. This is the “overhead” of polymorphism.

Abstract Classes and Pure Virtual Functions

A pure virtual function has no implementation and is declared with = 0. Classes with pure virtuals are abstract: cannot be instantiated.

class Drawable {
public:
    virtual void draw() const = 0;  // Pure virtual
    virtual ~Drawable() = default;
};

class Rectangle : public Drawable {
public:
    void draw() const override {
        // Implementation
    }
};

// Drawable d;  // Error: cannot instantiate abstract class
Rectangle r;    // OK: Rectangle is concrete

Interface idiom: Abstract class with only pure virtuals and no data members.

The Override and Final Specifiers

C++11 added override and final to catch errors:

class Base {
public:
    virtual void foo(int x);
    virtual void bar() const;
};

class Derived : public Base {
public:
    void foo(double x) override;  // Error: doesn't override (different signature)
    void bar() override;          // Error: doesn't override (missing const)
};

final prevents further overriding or derivation:

class Base {
public:
    virtual void method() final;  // Cannot be overridden
};

class Derived final : public Base {  // Cannot be further derived
};

Virtual Destructors: A Critical Rule

If a class has virtual functions, its destructor must be virtual. Otherwise, deleting a derived object through a base pointer causes undefined behavior:

class Base {
public:
    ~Base() { std::cout << "Base destroyed\\n"; }
};

class Derived : public Base {
    int* data_;
public:
    Derived() : data_(new int[100]) {}
    ~Derived() { delete[] data_; }  // CRITICAL: must run
};

Base* p = new Derived;
delete p;  // UB: Only Base destructor runs, memory leak!

Solution: Make base destructor virtual:

class Base {
public:
    virtual ~Base() { }  // Virtual destructor
};

Slicing Problem

Assigning a derived object to a base object slices off the derived parts:

class Base { int x_; };
class Derived : public Base { int y_; };

Derived d;
Base b = d;  // Slicing: y_ is lost

Base& ref = d;  // OK: Reference preserves dynamic type
Base* ptr = &d;  // OK: Pointer preserves dynamic type

Rule: Pass polymorphic types by pointer or reference, never by value.

Conceptual Check

What happens if a base class destructor is NOT virtual?

Runtime Environment

Interactive Lab

1#include <iostream>
2#include <memory>
3 
4class Animal {
5public:
6 virtual void speak() const { std::cout << "...\n"; }
7 virtual ~Animal() = default;
8};
9 
10class Cat : public Animal {
11public:
12 void speak() const override { std::cout << "Meow!\n"; }
13};
14 
15void communicate(const Animal& a) {
16 a.speak(); // Dynamic dispatch
17}
18 
19int main() {
20 Cat c;
21 communicate(c);
22 return 0;
23}
System Console

Waiting for signal...