C++ and Object-Oriented Programming

Types of Inheritance; Polymorphism; Virtual Functions

C-CAT

Types of Inheritance

13.1 Single Inheritance

One base class → One derived class

class Vehicle { };
class Car : public Vehicle { };    // Car inherits from Vehicle

13.2 Multiple Inheritance

One derived class ← Multiple base classes

class FlyingMachine {
public:
    void fly() { cout << "Flying...\n"; }
};

class MotorVehicle {
public:
    void drive() { cout << "Driving...\n"; }
};

// FlyingCar inherits from both
class FlyingCar : public FlyingMachine, public MotorVehicle {
public:
    void demonstrate() {
        fly();    // from FlyingMachine
        drive();  // from MotorVehicle
    }
};

Diamond Problem: If both base classes inherit from the same grandparent, ambiguity occurs. Solved using virtual inheritance.

13.3 Multilevel Inheritance

Grandparent → Parent → Child
class Animal { };
class Mammal : public Animal { };        // Mammal inherits Animal
class Dog : public Mammal { };           // Dog inherits Mammal (and Animal)

13.4 Hierarchical Inheritance

         Base
        /    \
    Child1   Child2
class Shape { };
class Circle : public Shape { };
class Rectangle : public Shape { };
class Triangle : public Shape { };

13.5 Hybrid Inheritance

Combination of two or more types.

class A { };
class B : public A { };           // Single
class C : public A { };           // Single
class D : public B, public C { }; // Multiple (+ diamond problem!)

Solving Diamond with virtual:

class A { public: int x; };
class B : virtual public A { };   // virtual inheritance
class C : virtual public A { };
class D : public B, public C { };  // only one copy of A's members

Polymorphism

What is Polymorphism?

Polymorphism = "many forms" — the same interface used with different types, producing different results.

Types:

  1. Compile-time polymorphism — resolved at compile time

    • Function overloading
    • Operator overloading
    • Templates
  2. Runtime polymorphism — resolved at runtime

    • Virtual functions
    • Function overriding through base class pointers

Function Overriding

class Shape {
public:
    virtual double area() {   // virtual enables runtime polymorphism
        return 0;
    }
    virtual void display() {
        cout << "I am a shape with area: " << area() << endl;
    }
};

class Circle : public Shape {
private:
    double radius;
public:
    Circle(double r) :
radius(r) {}

    double area() override {   // override parent method
        return
3.14159 * radius * radius;
    }
};

class Rectangle : public Shape {
private:
    double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}

    double area() override {
return width * height;
    }
};

class Triangle : public Shape {
private:
    double base, height;
public:
    Triangle(double b, double h) : base(b), height(h) {}

    double area() override {
        return 0.5 * base * height;
    }
};

Virtual Functions

How Runtime Polymorphism Works

Shape *shapes[] = {
    new Circle(5.0),
    new Rectangle(4.0, 6.0),
    new Triangle(3.0, 8.0)
};

// Runtime polymorphism!
for (int i = 0; i < 3; i++) {
    shapes[i]->display();   // Calls
correct area() for each object type
}

// Output:
// I am a shape with area: 78.5398 (Circle)
// I am a shape with area: 24      (Rectangle)
// I am a shape with area: 12      (Triangle)

Why virtual?

Without virtual:

Shape *s = new Circle(5.0);
s->area();   // Without virtual: calls Shape::area() → 0 (WRONG!)
             // With virtual: calls Circle::area() → 78.5 (CORRECT!)

Vtable (Virtual Table):

Each class with virtual functions has a VTABLE (table of function pointers)
Each object has a VPTR (pointer to its class's vtable)

Shape vtable:    [area → Shape::area]
Circle vtable:   [area → Circle::area]
Rectangle
vtable:[area → Rectangle::area]

When you call s->area():
  s has a VPTR pointing to Circle's vtable
  → Calls Circle::area()

Virtual Destructor

Always make destructor virtual in base classes:

class Base {
public:
    virtual ~Base() {   // virtual destructor!
        cout << "Base destructor\n";
    }
};

class Derived : public Base {
private:
    int *data;
public:
    Derived() { data = new
int[100]; }
    ~Derived() {
        delete[] data;   // If Base destructor not virtual,
this may not be called!
        cout << "Derived destructor\n";
    }
};

Base *b = new Derived();
delete b;   // Without virtual: only Base destructor called → MEMORY LEAK!
            // With virtual: Derived destructor called first → SAFE

Continue learning

Related notes

Put this topic into timed practice

Open mock tests when you want full-exam pacing, or keep drilling in practice mode.