Unit 3

Inheritance & Polymorphism

From zero to full mastery — internal memory mechanics, real code, exam prep

← Home
01
Inheritance Basics
02
Access Specifiers
03
Constructor Order
04
Friend & Inline
05
Virtual Functions
06
Abstract Classes
07
UML Diagrams
08
Exam Questions
C++Compiler
01

Inheritance — From Zero

Real-Life AnalogyA child inherits traits from parents — eye color, height, surname. In C++, a Derived class inherits members from a Base class. You DON'T rewrite everything — you EXTEND.

What is Inheritance?

Inheritance is an OOP mechanism where a new class (derived/child) acquires properties and behaviors from an existing class (base/parent). The core idea: reuse + extend.

Syntax

#include <iostream>
using namespace std;

class Base {
public:
    int x;
    void show() { cout << "Base show: " << x << "\n"; }
};

class Derived : public Base {  // colon = "inherits from"
public:
    int y;
    void display() { cout << "Derived display: " << y << "\n"; }
};

int main() {
    Derived d;
    d.x = 10;
    d.y = 20;
    d.show();
    d.display();
    return 0;
}
▶ Expected Output
Base show: 10 Derived display: 20

Types of Inheritance

Type Syntax Meaning
Single class B : public A One derived from one base
Multiple class C : public A, public B One derived from many bases
Multilevel A → B → C Chain of inheritance
Hierarchical A → B, A → C Many derived from one base
Hybrid Mix of above Combination (can cause diamond problem)

Single Inheritance — Full Example

#include <iostream>
#include <string>
using namespace std;

// Base class
class Animal {
public:
    string name;
    void eat() { cout << name << " is eating\n"; }
};

// Derived class inherits Animal
class Dog : public Animal {
public:
    void bark() { cout << name << " says Woof!\n"; }
};

int main() {
    Dog d;
    d.name = "Rex";   // inherited from Animal
    d.eat();           // inherited method
    d.bark();          // own method
    return 0;
}
▶ Expected Output
Rex is eating Rex says Woof!

Multiple Inheritance

#include <iostream>
using namespace std;

class Father { public: void wealth() { cout << "Rich\n"; } };
class Mother { public: void talent() { cout << "Creative\n"; } };

class Child : public Father, public Mother {
public:
    void intro() { wealth(); talent(); }
};

int main() {
    Child c;
    c.intro();
    return 0;
}
▶ Expected Output
Rich Creative
Diamond ProblemWhen two base classes inherit from same grandparent, and a class inherits from both — ambiguity occurs. Fix: use virtual inheritance: class B : virtual public A

Multilevel Inheritance

#include <iostream>
using namespace std;

class Grandparent { public: void gp() { cout << "GP\n"; } };
class Parent : public Grandparent { public: void p() { cout << "P\n"; } };
class Child : public Parent { public: void c() { cout << "C\n"; } };

int main() {
    Child ch;
    // Child has access to gp(), p(), and c()
    ch.gp();
    ch.p();
    ch.c();
    return 0;
}
▶ Expected Output
GP P C
Memory Trick — Types of Inheritance"Some Men Meet Hungry Hipppos" → Single, Multiple, Multilevel, Hierarchical, Hybrid
class A { public: int x; };
class B : public A { public: int y; }; // Single
class C : public B { public: int z; }; // Multilevel
► Expected Output
1
Single Inheritance: One parent, one child.
2
Multilevel: Chain of inheritance (A->B->C).
3
Derived classes access all public/protected base members.
🔨 Build From Scratch — Inheritance Basics
class Base {}; class Derived : public Base {};
► Expected Output
1
Foundation of reuse.
02

Access Specifiers in Inheritance

AnalogyThink of a family house. Public rooms (living room) everyone can access. Protected rooms (bedroom) only family and guests can access. Private rooms (safe) only the owner can access.

Member Access in Classes

Specifier Same Class Derived Class Outside World
public
protected
private

Inheritance Mode Effect

How members are inherited depends on the inheritance mode used in the derived class:

Base Member public inheritance protected inheritance private inheritance
public public protected private
protected protected protected private
private Not inherited Not inherited Not inherited
Key RulePrivate members of base class are NEVER directly accessible in derived class — not even with public inheritance. Use public/protected methods to access them.

Code Example

#include <iostream>
using namespace std;

class Base {
public:    int pub = 1;
protected: int prot = 2;
private:   int priv = 3;
};

class D1 : public Base {
public:
    void test() {
        pub  = 10;  // OK — still public
        prot = 20;  // OK — still protected
        // priv = 30; ERROR — not accessible
        cout << "D1 test: pub=" << pub << " prot=" << prot << "\n";
    }
};

class D2 : private Base {
public:
    void test() {
        pub  = 10;  // OK — but now private in D2
        prot = 20;  // OK — but now private in D2
        cout << "D2 test: pub=" << pub << " prot=" << prot << "\n";
    }
};

int main() {
    D1 d1;
    d1.pub = 5;   // OK — public inheritance keeps it public
    d1.test();

    D2 d2;
    // d2.pub = 5; ERROR — private inheritance makes it private
    d2.test();
    return 0;
}
▶ Expected Output
D1 test: pub=10 prot=20 D2 test: pub=10 prot=20
Memory Trick — Access Rule"Public stays Public, Protected stays Protected, Private stays PRIVATE (hidden forever)" — for public inheritance.
class Base { private: int a; protected: int b; public: int c; };
class Derived : public Base { void test(){ b=1; c=2; /* a=3; ERROR */ } };
► Expected Output
1
Public Inheritance: Public remains Public, Protected remains Protected.
2
Private members are NEVER inherited.
3
Protected members are accessible in derived classes but private to the world.
03

Constructor & Destructor Order

AnalogyBuilding a house: first lay the foundation (base constructor), then build walls (derived constructor). Demolishing: tear down walls first (derived destructor), then foundation (base destructor). Always reverse order!

The Golden Rule

Order of Execution

Construction: Base constructor → Derived constructor

Destruction: Derived destructor → Base destructor

#include <iostream>
using namespace std;

class Base {
public:
    Base()  { cout << "Base Constructor\n"; }
    ~Base() { cout << "Base Destructor\n"; }
};

class Derived : public Base {
public:
    Derived()  { cout << "Derived Constructor\n"; }
    ~Derived() { cout << "Derived Destructor\n"; }
};

int main() {
    cout << "--- Creating d ---\n";
    {
        Derived d;
        cout << "--- d goes out of scope ---\n";
    }
    return 0;
}
▶ Expected Output
--- Creating d --- Base Constructor Derived Constructor --- d goes out of scope --- Derived Destructor Base Destructor

Passing Arguments to Base Constructor

Use initializer list syntax to pass arguments to base class constructor:

#include <iostream>
using namespace std;

class Base {
    int x;
public:
    Base(int a) : x(a) { cout << "Base(" << x << ")\n"; }
};

class Derived : public Base {
    int y;
public:
    Derived(int a, int b) : Base(a), y(b) {  // Base(a) called first
        cout << "Derived(" << y << ")\n";
    }
};

int main() {
    Derived d(10, 20);
    return 0;
}
▶ Expected Output
Base(10) Derived(20)

Multiple Inheritance Constructor Order

#include <iostream>
using namespace std;

class A { public: A() { cout << "A\n"; } };
class B { public: B() { cout << "B\n"; } };
class C : public A, public B {
public:
    C() { cout << "C\n"; }
};

int main() {
    C c;
    return 0;
}
▶ Expected Output
A B C
Common Exam TrapThe order of constructors follows the order of base classes in the class declaration (class C : public A, public B), NOT the order in the initializer list!
Memory Trick"BABA" — Build Ascending, Break Ascending reversed = Build Base then Derived, Destroy Derived then Base
class Base { public: Base(){ cout<<"B"; } };
class Derived : public Base { public: Derived(){ cout<<"D"; } };
int main(){ Derived obj; }
► Expected Output
BD
1
Base constructor runs FIRST.
2
Derived constructor runs SECOND.
3
Order is reversed for destructors.
04

Friend Functions & Inline Functions

Friend Functions

AnalogyA trusted friend who has a key to your private house. A friend function is NOT a member of the class but has access to its private and protected members.

Key Facts

• Declared inside class with friend keyword, defined outside
• NOT a member function — called without object
• Has access to private & protected members
• Can be friend of multiple classes
• NOT inherited by derived classes

#include <iostream>
using namespace std;

class Box {
    int length;  // private
public:
    Box(int l) : length(l) {}
    friend void printLength(Box b);  // friend declaration
};

void printLength(Box b) {
    cout << "Length: " << b.length << "\n";  // accessing private!
}

int main() {
    Box b(10);
    printLength(b);  // called without object
    return 0;
}
▶ Expected Output
Length: 10

Friend Class

#include <iostream>
using namespace std;

class Engine;  // forward declaration

class Car {
    int speed = 200;
public:
    friend class Engine;  // Engine can access Car's privates
};

class Engine {
public:
    void tune(Car& c) {
        cout << "Speed: " << c.speed << "\n";  // OK — Engine is friend of Car
    }
};

int main() {
    Car myCar;
    Engine myEngine;
    myEngine.tune(myCar);
    return 0;
}
▶ Expected Output
Speed: 200
Important — Friend is NOT symmetric!If A is friend of B, it does NOT mean B is friend of A. Friendship must be explicitly declared.

Inline Functions

AnalogyInstead of calling a store across town (function call overhead), you have a mini-store inside your house (inline = code pasted directly at call site).

The inline keyword requests the compiler to replace a function call with the actual function code — eliminating call overhead.

#include <iostream>
using namespace std;

inline int square(int x) { return x * x; }

int main() {
    cout << square(5) << "\n";  // compiler replaces with: cout << 5*5;
    return 0;
}
▶ Expected Output
25
Inline Normal Function
No call overhead Stack frame created
Code duplicated at each call One copy in memory
Best for small, frequent functions Best for large functions
Compiler may ignore inline Always respected
Auto-inlineFunctions defined inside a class body are automatically inline — no need to write the keyword.
Memory Trick — Friend"Friend has Full access but is not Family (not a member, not inherited)"
class Box { int width; friend void setWidth(Box &b, int w); };
void setWidth(Box &b, int w){ b.width = w; }
► Expected Output
1
Friend function is NOT a member function.
2
Can access private/protected members.
3
Declared inside class with friend keyword.
05

Virtual Functions & Runtime Polymorphism

AnalogyYou call "speak()" on any animal. A Dog barks, a Cat meows, a Bird chirps — same function call, different behavior based on actual object type. This is polymorphism — "many forms".

The Problem Without virtual

#include <iostream>
using namespace std;

class Animal {
public:
    void speak() { cout << "Animal speaks\n"; }
};

class Dog : public Animal {
public:
    void speak() { cout << "Dog barks\n"; }
};

int main() {
    Animal* ptr = new Dog();
    ptr->speak();  // Output: "Animal speaks" ← WRONG!
    // Without virtual, base class version always called via pointer
    delete ptr;
    return 0;
}
▶ Expected Output
Animal speaks

Solution: virtual Keyword

#include <iostream>
using namespace std;

class Animal {
public:
    virtual void speak() { cout << "Animal speaks\n"; }
};

class Dog : public Animal {
public:
    void speak() override { cout << "Dog barks\n"; }
};

class Cat : public Animal {
public:
    void speak() override { cout << "Cat meows\n"; }
};

int main() {
    Animal* p1 = new Dog();
    Animal* p2 = new Cat();
    p1->speak();  // Dog barks ✓
    p2->speak();  // Cat meows ✓
    // Decision made at RUNTIME — Runtime Polymorphism
    
    delete p1;
    delete p2;
    return 0;
}
▶ Expected Output
Dog barks Cat meows

How it Works — VTable (Internal Mechanics)

Virtual Table (VTable)

Every class with virtual functions has a hidden VTable — a table of function pointers.
Every object has a hidden vptr — a pointer to its class's VTable.
When you call ptr->speak(), the runtime looks up the VTable via vptr and calls the correct function.

Memory Layout:
Animal object: [ vptr → Animal::VTable [ speak → Animal::speak ] ]
Dog object:    [ vptr → Dog::VTable    [ speak → Dog::speak    ] ]

So ptr-speak() goes: ptr → vptr → VTable → correct function

virtual Destructor — CRITICAL

#include <iostream>
using namespace std;

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

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

int main() {
    Base* p = new Derived();
    delete p;
    // Without virtual destructor: only Base destroyed → MEMORY LEAK
    // With virtual destructor: Derived destroyed, then Base ✓
    return 0;
}
▶ Expected Output
Derived destroyed Base destroyed
RuleALWAYS make destructor virtual if you use polymorphism (base pointer to derived object). Memory leaks otherwise!

Function Overriding vs Overloading

Feature Overloading Overriding
Where Same class Base & Derived class
Signature Different Same
When resolved Compile time Runtime (with virtual)
Keyword None needed virtual + override
Memory Trick — Virtual"Virtual = VTable = Very late decision" — decision at runtime, not compile time
class Base { public: virtual void show(){ cout<<"B"; } };
class Derived : public Base { public: void show(){ cout<<"D"; } };
int main(){ Base *p; Derived d; p=&d; p->show(); }
► Expected Output
D
1
Virtual allows runtime binding.
2
VTable stores pointers to actual functions.
3
Base pointer can call derived methods.
06

Pure Virtual Functions & Abstract Classes

AnalogyA blueprint for a building is not a building itself — you can't live in it. An abstract class is a blueprint: it defines what derived classes MUST have, but cannot be instantiated itself.

Pure Virtual Function

A pure virtual function has no body — it FORCES derived classes to implement it:

#include <iostream>
using namespace std;

class Animal {
public:
    virtual void speak() = 0;  // "= 0" makes it pure virtual
};

class Dog : public Animal {
public:
    void speak() override { cout << "Woof\n"; }
};

int main() {
    Dog d;
    d.speak();
    return 0;
}
▶ Expected Output
Woof

Abstract Class Rules

Rules you MUST know

• A class with at least ONE pure virtual function = Abstract class
• Abstract class CANNOT be instantiated (no objects)
• Derived class MUST override ALL pure virtual functions
• If derived class doesn't override all → it also becomes abstract
• Abstract class CAN have constructors (called when derived is created)
• Abstract class CAN have non-pure (regular) virtual functions

#include <iostream>
using namespace std;

class Shape {  // Abstract class
public:
    virtual double area() = 0;      // pure virtual
    virtual double perimeter() = 0; // pure virtual
    virtual void draw() { cout << "Drawing shape\n"; }  // regular virtual
};

class Circle : public Shape {
    double r;
public:
    Circle(double r) : r(r) {}
    double area() override      { return 3.14 * r * r; }
    double perimeter() override { return 2 * 3.14 * r; }
};

class Rectangle : public Shape {
    double w, h;
public:
    Rectangle(double w, double h) : w(w), h(h) {}
    double area() override      { return w * h; }
    double perimeter() override { return 2 * (w + h); }
};

int main() {
    // Shape s;  ← ERROR: cannot instantiate abstract class

    Shape* shapes[] = { new Circle(5), new Rectangle(4,6) };

    for (auto s : shapes) {
        cout << "Area: " << s->area() << "\n";
    }

    for (auto s : shapes) delete s; // Cleanup
    return 0;
}
▶ Expected Output
Area: 78.5 Area: 24

Interface in C++ (100% Abstract)

C++ doesn't have the interface keyword like Java, but you can simulate it — a class with ALL pure virtual functions acts as an interface:

#include <iostream>
using namespace std;

class IPrintable {  // "Interface"
public:
    virtual void print() = 0;
    virtual void save() = 0;
    virtual ~IPrintable() = default;
};

class Document : public IPrintable {
public:
    void print() override { cout << "Printing Document\n"; }
    void save() override { cout << "Saving Document\n"; }
};

int main() {
    Document doc;
    doc.print();
    doc.save();
    return 0;
}
▶ Expected Output
Printing Document Saving Document
Memory Trick"= 0 means zero body, zero objects allowed" — pure virtual = no implementation, abstract class = no instances
class Shape { public: virtual void draw()=0; };
class Circle : public Shape { public: void draw(){ cout<<"Draw"; } };
► Expected Output
1
Pure virtual function (=0) makes class abstract.
2
Cannot instantiate abstract class.
3
Forces derived classes to implement behavior.
07

UML Class Diagrams

UML Box Structure

A class in UML has 3 compartments

+------------------+
|   ClassName      |  ← Class Name (bold)
+------------------+
| - privateAttr    |  ← Attributes
| # protectedAttr  |  
| + publicAttr     |
+------------------+
| + publicMethod() |  ← Methods
| - privateMethod()|
+------------------+

Visibility symbols: + public   - private   # protected

Relationships in UML

Relationship Symbol Meaning C++ Code
Inheritance → (solid line, hollow arrow) "is-a" class Dog : Animal
Association — (solid line) "uses-a" pointer/ref member
Aggregation ◇— (hollow diamond) "has-a" (weak) object passed in
Composition ◆— (filled diamond) "part-of" (strong) object member
Dependency --→ (dashed arrow) "depends-on" used in method

Reading a UML Diagram — Example

         +----------+
         |  Animal  |         ← Abstract (italic name)
         +----------+
         | # name   |
         +----------+
         | + speak()*|         ← * = pure virtual
         +----------+
              △               ← Inheritance arrow
       _______|_______
      |                |
 +---------+      +---------+
 |   Dog   |      |   Cat   |
 +---------+      +---------+
 | +speak()|      | +speak()|
 +---------+      +---------+
Exam TipIn UML, abstract class names and methods are written in italics. Pure virtual methods have an asterisk (*) or are labeled {abstract}.
Memory Trick — Visibility"+ is positive/public, - is negative/private, # is hash/protected" — like social media privacy settings!
08

Exam Questions & Quick Quiz

2-Mark Questions

Q1. What is inheritance? Name its types.
Inheritance is acquiring properties of a base class in a derived class. Types: Single, Multiple, Multilevel, Hierarchical, Hybrid.
Q2. What is a virtual function?
A function declared with virtual keyword in base class, overridden in derived. Enables runtime polymorphism — correct version selected based on actual object type at runtime via VTable.
Q3. What is a pure virtual function?
A virtual function with = 0, having no body. Forces derived classes to implement it. Makes the class abstract.
Q4. What is the order of constructor/destructor calls in inheritance?
Constructor: Base → Derived. Destructor: Derived → Base. (Reverse for destruction)
Q5. Difference between function overloading and overriding?
Overloading: same name, different signature, same class, compile-time. Overriding: same name, same signature, different class (base/derived), runtime (with virtual).

5-Mark Questions

Q6. Explain access specifiers with table and example.
public: accessible everywhere. protected: class + derived. private: class only. In inheritance: public mode keeps access levels, protected mode makes public→protected, private mode makes all→private. Private base members are never inherited.
Q7. Write a program demonstrating runtime polymorphism using virtual functions.
Create base class Shape with virtual area(). Create derived classes Circle and Rectangle overriding area(). Use base pointer to call area() — correct version selected at runtime via VTable lookup.
Q8. What is an abstract class? How is it different from a concrete class?
Abstract class: has ≥1 pure virtual function, cannot be instantiated, acts as blueprint. Concrete class: all methods implemented, can create objects. Derived classes must override all pure virtual functions or remain abstract.

MCQ Rapid Fire

Which symbol is used for inheritance in UML?
Solid line with hollow triangle/arrow pointing to base class.
Can you create object of abstract class?
No. Attempting gives compile error: "cannot instantiate abstract class".
What happens if derived class doesn't override all pure virtual functions?
The derived class also becomes abstract — you cannot create its objects either.
Friend function is a member of the class — True or False?
False. It has access to private members but is NOT a member of the class.
What is diamond problem? How is it solved?
When two base classes inherit from same grandparent, and a class inherits from both — ambiguity arises. Solved using virtual inheritance: class B : virtual public A.

Revision Sheet — 1-Minute Recall

Unit 3 Key Points

Inheritance: reuse + extend. 5 types: S,M,ML,H,Hybrid
Access: public=everywhere, protected=family, private=self only
Constructor order: Base→Derived. Destructor: Derived→Base
Friend: not a member, has private access, not inherited
Inline: code pasted at call site, no overhead, good for small functions
Virtual: = 0 means pure, class abstract, VTable used at runtime
Abstract class: can't instantiate, blueprint, forces implementation
UML: + public, - private, # protected. △ = inheritance arrow


P1

Practice — Inheritance

✍️ Write From Scratch — Inheritance
Problem 1
Create a Vehicle → Car → ElectricCar multilevel hierarchy. Vehicle has brand and speed. Car adds fuelType. ElectricCar adds batteryRange. All have a describe() function. Create an ElectricCar object and call describe() showing ALL inherited info.
Think: who calls whose constructor? Who has access to what?
Show Solution ▾ class Vehicle { public: string brand; int speed; Vehicle(string b, int s) : brand(b), speed(s) {} virtual void describe() { cout << "Brand:" << brand << " Speed:" << speed << "\n"; } }; class Car : public Vehicle { public: string fuelType; Car(string b, int s, string f): Vehicle(b,s), fuelType(f) {} void describe() override { Vehicle::describe(); cout << "Fuel:" << fuelType << "\n"; } }; class ElectricCar : public Car { public: int batteryRange; ElectricCar(string b, int s, int r) : Car(b,s,"Electric"), batteryRange(r) {} void describe() override { Car::describe(); cout << "Range:" << batteryRange << "km\n"; } };
Problem 2
Implement multiple inheritance: class Flyable has fly(). class Swimmable has swim(). class Duck inherits both and overrides both. Create a Duck object and call both methods. Also print a combined action() that flies and swims together.
Show Solution ▾ class Flyable { public: virtual void fly() { cout << "Flying!\n"; } }; class Swimmable { public: virtual void swim() { cout << "Swimming!\n"; } }; class Duck : public Flyable, public Swimmable { public: void fly() override { cout << "Duck flies low!\n"; } void swim() override { cout << "Duck paddles!\n"; } void action() { fly(); swim(); } };
Problem 3
Write hierarchical inheritance: Shape → Circle, Rectangle, Triangle. Each must have getArea() and getPerimeter(). Use constructors with parameters. Create an array of 3 Shape pointers pointing to all three — loop and print area of each via virtual dispatch.
Show Solution ▾ class Shape { public: virtual double getArea() = 0; virtual ~Shape() {} }; class Circle : public Shape { double r; public: Circle(double r):r(r){} double getArea() override { return 3.14*r*r; } }; class Rectangle : public Shape { double w,h; public: Rectangle(double w,double h):w(w),h(h){} double getArea() override { return w*h; } }; int main() { Shape* shapes[] = {new Circle(5), new Rectangle(4,6)}; if(auto s:shapes) cout << s->getArea() << "\n"; }
🧠 How to Approach Inheritance Problems in Exam
1
Identify the "is-a" relationships — "A Dog IS-A Animal" = inheritance. "A Car HAS-A Engine" = composition, not inheritance.
2
Design the base class first — what data and methods are COMMON to all derived classes? Put ONLY those in base.
3
Choose access mode — Almost always use public inheritance for "is-a". Private = "implemented-in-terms-of". Protected = rarely used.
4
Constructor chain — Always write initializer list in derived constructor: Derived(args) : Base(base_args) {}
5
If polymorphism needed → add virtual to base methods + override in derived. Always add virtual destructor to base!
P2

Predict Output — Constructor & Virtual Dispatch

▶ Predict the Output
Question 1 — Constructor Chain
What is the exact output?
#include <iostream>
using namespace std;

class A {
public:
    A()  { cout << "A()\n"; }
    ~A() { cout << "~A()\n"; }
};
class B : public A {
public:
    B()  { cout << "B()\n"; }
    ~B() { cout << "~B()\n"; }
};
class C : public B {
public:
    C()  { cout << "C()\n"; }
    ~C() { cout << "~C()\n"; }
};
int main() { 
    C obj; 
    return 0;
}
Reveal Answer ▾ Output:
A()B()C()~C()~B()~A()

Constructors: Base→Derived (A→B→C). Destructors: reverse (C→B→A).
Question 2 — Virtual Dispatch Trap
What does this print?
#include <iostream>
using namespace std;

class Animal {
public:
    void speak()         { cout << "Animal\n"; }
    virtual void move()  { cout << "Animal moves\n"; }
};
class Dog : public Animal {
public:
    void speak()          { cout << "Dog\n"; }
    void move() override  { cout << "Dog runs\n"; }
};
int main() {
    Animal* p = new Dog();
    p->speak();
    p->move();
    delete p;
    return 0;
}
Reveal Answer ▾ Output:
AnimalDog runs

Explanation: speak() is NOT virtual → resolved at compile time via pointer type (Animal*) → prints "Animal". move() IS virtual → resolved at runtime via VTable based on actual object type (Dog) → prints "Dog runs". This is the most common exam trap!
Question 3 — Multiple Inheritance Order
What is the output?
#include <iostream>
using namespace std;

class X { public: X() { cout << "X "; } };
class Y { public: Y() { cout << "Y "; } };
class Z : public Y, public X {
public:
    Z() : X(), Y() { cout << "Z"; }
};
int main() { 
    Z z; 
    return 0; 
}
Reveal Answer ▾ Output: Y X Z

TRAP: The initializer list order : X(), Y() is IGNORED! Constructors are called in the order of inheritance declaration: class Z : public Y, public X → Y first, then X.
Question 4 — Virtual Destructor Trap
Is there a problem? What is the output?
#include <iostream>
using namespace std;

class Base {
public:
    ~Base() { cout << "~Base\n"; }   // NOT virtual!
};
class Derived : public Base {
public:
    ~Derived() { cout << "~Derived\n"; }
};
int main() {
    Base* p = new Derived();
    delete p;
    return 0;
}
Reveal Answer ▾ Output: ~Base ← ONLY base destructor called!

Problem: ~Derived() is NEVER called → memory leak if Derived has dynamically allocated resources. Fix: make Base destructor virtual: virtual ~Base(). Then output would be: ~Derived → ~Base (correct order).
P3

Debug This — Find & Fix the Bugs

🐞 Debug These Programs
Bug 1 — Abstract Class Instantiation
Find all errors and fix them:
#include <iostream>
using namespace std;

class Shape {
public:
    virtual double area() = 0;
};
class Circle : public Shape {
    double r;
public:
    Circle(double r) : r(r) {}
    // forgot to override area()
};
int main() {
    Shape s;          // line A
    Circle c(5);      // line B
    cout << c.area(); // line C
    return 0;
}
Show Fix ▾ Errors:
Line A: Cannot instantiate abstract class Shape (has pure virtual area())
Line B & C: Circle didn't override area() → Circle is also abstract → cannot instantiate

Fix: Remove line A. Add to Circle:
double area() override { return 3.14 * r * r; }
Bug 2 — Access Specifier Confusion
#include <iostream>
using namespace std;

class Base {
private:
    int x = 10;
public:
    int getX() { return x; }
};
class Derived : public Base {
public:
    void show() {
        cout << x;       // line A
        cout << getX();  // line B
    }
};

int main() {
    Derived d;
    d.show();
    return 0;
}
Show Fix ▾ Error: Line A — x is private in Base. Cannot access private member from derived class, even with public inheritance.

Fix: Line A is wrong. Line B is correct — use the public getter getX().
If direct access needed: change private: to protected: in Base.
Bug 3 — Diamond Problem
#include <iostream>
using namespace std;

class A { public: void hello() { cout << "A\n"; } };
class B : public A {};
class C : public A {};
class D : public B, public C {};

int main() {
    D d;
    d.hello(); // ERROR!
    return 0;
}
Show Fix ▾ Error: Ambiguous — D has TWO copies of A (one via B, one via C). Compiler doesn't know which hello() to call: B::A::hello() or C::A::hello()?

Fix Option 1 — Explicit scope:
d.B::hello(); or d.C::hello();

Fix Option 2 — Virtual inheritance (proper fix):
class B : virtual public A {};
class C : virtual public A {};
Now D has only ONE copy of A. d.hello() works without ambiguity.
Bug 4 — Missing Virtual Destructor
#include <iostream>
using namespace std;

class Animal {
public:
    int* data;
    Animal() { data = new int[100]; }
    ~Animal() { delete[] data; cout << "Animal cleaned\n"; }
};
class Dog : public Animal {
public:
    int* extra;
    Dog() { extra = new int[50]; }
    ~Dog() { delete[] extra; cout << "Dog cleaned\n"; }
};

int main() {
    Animal* p = new Dog();
    delete p; // MEMORY LEAK!
    return 0;
}
Show Fix ▾ Problem: ~Animal() is not virtual. When delete p called via Animal pointer, only Animal's destructor runs. Dog's destructor is NEVER called → extra array leaks (50 ints = 200 bytes lost).

Fix: Add virtual to Animal's destructor:
virtual ~Animal() { delete[] data; }
Now delete p → calls ~Dog() first → then ~Animal() → no leak.
P4

Edge Cases & Tricky Scenarios

⚠️ Edge Cases You MUST Know

1. Calling Virtual Function from Constructor

#include <iostream>
using namespace std;

class Base {
public:
    Base() { speak(); }  // calling virtual from constructor!
    virtual void speak() { cout << "Base speak\n"; }
};
class Derived : public Base {
public:
    void speak() override { cout << "Derived speak\n"; }
};
int main() { 
    Derived d; 
    return 0;
}
// Output: "Base speak" ← NOT "Derived speak"!
Why? During Base constructor execution, the Derived part hasn't been constructed yet. The VTable points to Base's version. Virtual dispatch does NOT work in constructors or destructors!

2. Slicing Problem

#include <iostream>
using namespace std;

class Animal { public: virtual void speak() { cout << "Animal\n"; } };
class Dog : public Animal { public: void speak() override { cout << "Dog\n"; } };

int main() {
    Animal a = Dog();  // OBJECT slicing — Dog part is cut off!
    a.speak();          // Prints "Animal" — Dog's speak() is LOST

    Animal* p = new Dog(); // pointer — NO slicing
    p->speak();              // Prints "Dog" — correct!
    delete p;
    return 0;
}
Rule: ALWAYS use pointers or references for polymorphism, NEVER copy by value!

3. Pure Virtual with Body

#include <iostream>
using namespace std;

class Base {
public:
    virtual void show() = 0;  // pure virtual CAN have a body!
};
void Base::show() { cout << "Base impl\n"; }  // defined outside

class Derived : public Base {
public:
    void show() override {
        Base::show();  // explicitly call base version
        cout << "Derived\n";
    }
};

int main() {
    Derived d;
    d.show();
    return 0;
}

A pure virtual function CAN have a body — but class is still abstract. Derived must still override it. The body can only be called via explicit scope resolution.

4. Overriding vs Hiding

#include <iostream>
using namespace std;

class Base {
public:
    virtual void show(int x) { cout << "Base: " << x; }
};
class Derived : public Base {
public:
    void show() { cout << "Derived"; }  // DIFFERENT signature!
    // This HIDES Base::show(int), NOT overrides it!
};

int main() {
    Derived d;
    // d.show(5); ERROR — Base::show(int) is hidden!
    d.Base::show(5);  // Only way to call it
    return 0;
}
Override vs Hide: If signature matches exactly = override (virtual dispatch works). If signature differs = hiding (Base version inaccessible from Derived). Use override keyword to let compiler catch this mistake!
P5

UML Practice — Draw & Interpret

📐 Step-by-Step: How to Draw UML from Code

Step 1: Identify Classes

List every class. Each becomes a UML box with 3 sections: name | attributes | methods.

Step 2: Mark Visibility

public → +, private → -, protected → #. Write type after colon: + name : string

Step 3: Draw Relationships

Inheritance = hollow triangle arrow (→) pointing TO base. Composition = filled diamond (◆). Association = plain line.

Step 4: Mark Abstract

Abstract class name and pure virtual methods in italics or with {abstract} label.

UML Problem 1 — Draw from Code
Draw the UML class diagram for this code:
class Person {
protected: string name; int age;
public:
    virtual void display() = 0;
    string getName();
};
class Student : public Person {
private: int rollNo;
public:
    void display() override;
    float getGPA();
};
class Teacher : public Person {
private: string subject;
public:
    void display() override;
    void teach();
};
Show UML ▾
#include 
using namespace std;


         +====================+
         |    <<abstract>>    |
         |       Person       |  ← abstract (italics in real UML)
         +====================+
         | # name : string    |
         | # age : int        |
         +--------------------+
         | + display() *      |  ← * = pure virtual
         | + getName() : str  |
         +====================+
                  △  (hollow triangle = inheritance)
         _________|__________
        |                    |
+==================+  +==================+
|     Student      |  |     Teacher      |
+==================+  +==================+
| - rollNo : int   |  | - subject : str  |
+------------------+  +------------------+
| + display()      |  | + display()      |
| + getGPA():float |  | + teach()        |
+==================+  +==================+
UML Problem 2 — Code from UML
Write C++ classes from this UML description:
• Abstract class Appliance: protected watts(int), pure virtual powerOn()
WashingMachine inherits Appliance: private capacity(int), overrides powerOn(), adds wash()
Refrigerator inherits Appliance: private temperature(float), overrides powerOn(), adds cool()
Show Code ▾ class Appliance { protected: int watts; public: Appliance(int w):watts(w){} virtual void powerOn() = 0; virtual ~Appliance(){} }; class WashingMachine : public Appliance { int capacity; public: WashingMachine(int w,int c):Appliance(w),capacity(c){} void powerOn() override { cout<<"WM ON\n"; } void wash() { cout<<"Washing\n"; } }; class Refrigerator : public Appliance { float temperature; public: Refrigerator(int w,float t):Appliance(w),temperature(t){} void powerOn() override { cout<<"Fridge ON\n"; } void cool() { cout<<"Cooling\n"; } };
P6

Active Recall — Test Yourself Without Notes

⏱ Close Your Notes — Answer These
Rapid Fire — 30 seconds each
1. Write the exact syntax to inherit B from A using public mode.
class B : public A { };
2. What does = 0 after a function declaration mean?
It makes the function pure virtual. The class becomes abstract and cannot be instantiated. Derived classes MUST override it.
3. Can an abstract class have a constructor?
YES. Abstract class can have constructors — they are called when a derived class object is created (via initializer list).
4. A class has 3 pure virtual functions. Derived overrides only 2. What happens?
Derived class also becomes abstract — cannot create objects of it until ALL pure virtual functions are overridden.
5. What is VTable? What contains it?
VTable (Virtual Table) is a table of function pointers for virtual functions. Every class with virtual functions has one. Each object has a hidden vptr pointing to its class's VTable.
6. What is the difference between overriding and hiding?
Overriding: same name + same signature in derived class, virtual dispatch works. Hiding: same name but DIFFERENT signature — base version is hidden, virtual dispatch does NOT work for hidden function.
7. Why must destructor be virtual in polymorphism?
When deleting via base pointer, non-virtual destructor only calls base destructor. Derived destructor is skipped → memory leak. Virtual destructor ensures proper cleanup chain: derived → base.
8. Friend function — is it inherited? Can it access private members?
NOT inherited — friendship is not passed to derived classes. YES it can access private members — that's its purpose. It's declared inside the class but is NOT a member.
9. What is inline function? When does the compiler ignore inline?
Function whose body is substituted at call site, eliminating call overhead. Compiler ignores inline for: recursive functions, functions with loops, large functions, functions whose address is taken.
10. Write the UML visibility symbols for public, private, protected.
+ (plus) = public, - (minus) = private, # (hash) = protected.
11. In multiple inheritance class C : public A, public B — which constructor is called first?
A's constructor — order follows the inheritance declaration (left to right): A first, then B, then C. The initializer list order is irrelevant.
12. What is the diamond problem? One sentence.
When two classes B and C both inherit from A, and D inherits from both B and C, D ends up with two copies of A causing ambiguity. Solved using virtual inheritance.
🎯

Final Exam Coding Programs

12 full exam-level programs. Attempt without looking at solution first.

Program 01
Bank Account Hierarchy
10 marks
Abstract class Account (balance, accountNo). Pure virtual: calculateInterest(), display(). Derived: SavingsAccount (rate), CurrentAccount (overdraftLimit). Create both, display via base pointer array.
Show Solution ▾
#include 
using namespace std;

class Account {
protected: double balance; int accountNo;
public:
    Account(int no, double bal): accountNo(no), balance(bal){}
    virtual double calculateInterest() = 0;
    virtual void display() = 0;
    virtual ~Account(){}
};
class SavingsAccount : public Account {
    double rate;
public:
    SavingsAccount(int no, double bal, double r): Account(no,bal), rate(r){}
    double calculateInterest() override { return balance*rate/100; }
    void display() override { cout "Savings Acc#" " Interest:" calculateInterest() "\n"; }
};
int main() {
    Account* a = new SavingsAccount(101, 10000, 4.5);
    a-display();
    delete a;
    return 0;
}
Program 02
Employee Payroll System
10 marks
Abstract Employee (name, id). Pure virtual calculateSalary(). Derived: FullTimeEmployee (monthlySalary), PartTimeEmployee (hourlyRate, hours), Manager (base+bonus). Show all via base pointer array.
Show Solution ▾
#include 
using namespace std;

class Employee {
protected: string name; int id;
public:
    Employee(string n,int i):name(n),id(i){}
    virtual double calculateSalary()=0;
    virtual void display(){cout<<name<<" Salary:"<<calculateSalary()<<"\n";}
    virtual ~Employee(){}
};
class FullTime : public Employee {
    double salary;
public:
    FullTime(string n,int i,double s):Employee(n,i),salary(s){}
    double calculateSalary() override {return salary;}
};
class PartTime : public Employee {
    double rate; int hours;
public:
    PartTime(string n,int i,double r,int h):Employee(n,i),rate(r),hours(h){}
    double calculateSalary() override {return rate*hours;}
};
int main() {
    Employee* e[]={new FullTime("Alice",1,50000),new PartTime("Bob",2,200,80)};
    if(auto x:e){x-display();delete x;}
    return 0;
}
Program 03
Friend Function — Two Class Volume Compare
5 marks
Box (private l,w,h) and Cylinder (private r,h). Friend function compare(Box, Cylinder) calculates volumes of both and prints which is larger. Forward declare Cylinder before Box.
Show Solution ▾
#include 
using namespace std;

class Cylinder;
class Box {
    double l,w,h;
public:
    Box(double l,double w,double h):l(l),w(w),h(h){}
    friend void compare(Box b,Cylinder c);
};
class Cylinder {
    double r,h;
public:
    Cylinder(double r,double h):r(r),h(h){}
    friend void compare(Box b,Cylinder c);
};
void compare(Box b, Cylinder c) {
    double bv=b.l*b.w*b.h, cv=3.14*c.r*c.r*c.h;
    cout<<(bvcv?"Box":"Cylinder")<<" is larger\n";
}
int main(){Box b(3,4,5);Cylinder c(3,7);compare(b,c);    return 0;
}
Program 04
Runtime Polymorphism — Media Player
8 marks
Abstract Media (title, duration). Pure virtual play() and getInfo(). Derived: Song (artist, genre), Movie (director, rating). Use base pointer array of 4 items (mix of songs and movies). Loop and call play() on each — shows runtime dispatch.
Show Solution ▾
#include 
using namespace std;

class Media {
protected: string title; int duration;
public:
    Media(string t,int d):title(t),duration(d){}
    virtual void play()=0;
    virtual void getInfo()=0;
    virtual ~Media(){}
};
class Song : public Media {
    string artist;
public:
    Song(string t,int d,string a):Media(t,d),artist(a){}
    void play() override { cout<<"Playing song: "<<title<<" by "<<artist<<"\n"; }
    void getInfo() override { cout<<title<<" | "<<duration<<"s\n"; }
};
class Movie : public Media {
    string director;
public:
    Movie(string t,int d,string dir):Media(t,d),director(dir){}
    void play() override { cout<<"Playing movie: "<<title<<" dir:"<<director<<"\n"; }
    void getInfo() override { cout<<title<<" | "<<duration<<"min\n"; }
};
int main(){
    Media* lib[]={new Song("Blinding Lights",200,"Weeknd"),new Movie("Inception",148,"Nolan")};
    if(auto m:lib){m-play();delete m;}
    return 0;
}
Program 05
Diamond Problem — Virtual Inheritance Fix
8 marks
Grandparent class A with greet(). B and C both inherit from A. D inherits from B and C. Without virtual inheritance show the problem. Then fix using virtual inheritance and confirm single copy of A exists. Show both wrong and correct versions.
Show Solution ▾
#include 
using namespace std;

// WRONG (ambiguous):
class A {public: void greet(){cout<<"Hello from A\n";}};
class B:public A{};
class C:public A{};
class D:public B,public C{};
// D d; d.greet(); // ERROR: ambiguous

// CORRECT (virtual inheritance):
class A {public: void greet(){cout<<"Hello from A\n";}};
class B: virtual public A{};
class C: virtual public A{};
class D: public B, public C{};
int main(){D d; d.greet();} // OK: one copy of A
Program 06
Constructor Tracing — Predict & Verify
5 marks
Classes A, B:A, C:B. Each constructor prints "X created", each destructor prints "X destroyed". Main creates C object, trace exact output order for both construction and destruction. Extend with parameterized constructor passing values up the chain.
Show Solution ▾
#include 
using namespace std;

class A {int x;
public:
    A(int x):x(x){cout<<"A("<<x<<") created\n";}
    ~A(){cout<<"A destroyed\n";}};
class B:public A {int y;
public:
    B(int x,int y):A(x),y(y){cout<<"B("<<y<<") created\n";}
    ~B(){cout<<"B destroyed\n";}};
class C:public B {int z;
public:
    C(int x,int y,int z):B(x,y),z(z){cout<<"C("<<z<<") created\n";}
    ~C(){cout<<"C destroyed\n";}};
int main(){C obj(1,2,3);}  // Output: A(1)-B(2)-C(3)-~C-~B-~A
Program 07
Proper Access Design — Library System
8 marks
Base LibraryItem (private: itemId, protected: title and author, public: getters and virtual display()). Derived Book (private: ISBN, pages) and Magazine (private: issueNumber, month) — both override display() using protected base members directly. Show correct access patterns.
Show Solution ▾
#include 
using namespace std;

class LibraryItem {
    int itemId;          // private - only this class
protected:
    string title, author; // accessible by derived
public:
    LibraryItem(int id,string t,string a):itemId(id),title(t),author(a){}
    int getId(){return itemId;}
    virtual void display()=0;
    virtual ~LibraryItem(){}
};
class Book : public LibraryItem {
    string ISBN; int pages;
public:
    Book(int id,string t,string a,string isbn,int p):LibraryItem(id,t,a),ISBN(isbn),pages(p){}
    void display() override {
        // title, author accessible (protected)
        cout<<"Book: "<<title<<" by "<<author<<" ISBN:"<<ISBN<<"\n";
    }
};
int main(){
    LibraryItem* items[]={new Book(1,"C++ Primer","Lippman","978-x",976)};
    if(auto i:items){i-display();delete i;}
    return 0;
}
Program 08
Pure Interface Pattern — Smart Device
8 marks
Create interface IControllable (pure virtual: turnOn(), turnOff(), getStatus()). Create interface ISpeakable (pure virtual: speak()). Class SmartSpeaker inherits BOTH interfaces. Implement all methods. Demonstrate calling all methods via each interface pointer type.
#include 
using namespace std;

class IControllable {
public:
    virtual void turnOn()=0;
    virtual void turnOff()=0;
    virtual string getStatus()=0;
    virtual ~IControllable(){}};
class ISpeakable {
public:
    virtual void speak()=0;
    virtual ~ISpeakable(){}};
class SmartSpeaker:public IControllable,public ISpeakable{
    bool on=false;
public:
    void turnOn() override{on=true;cout<<"Speaker ON\n";}
    void turnOff() override{on=false;cout<<"Speaker OFF\n";}
    string getStatus() override{return on?"ON":"OFF";}
    void speak() override{cout<<"Playing music!\n";}};
int main(){
    SmartSpeaker s;
    IControllable* ctrl=&s;
    ISpeakable* spk=&s;
    ctrl-turnOn();
    spk-speak();
    ctrl-turnOff();
    return 0;
}
Program 09 — Challenge
Write From Scratch — University System
15 marks
Attempt this completely on your own: Design a university system with: Abstract Person (name, age, virtual display()). Student (rollNo, gpa, branch — overrides display()). Professor (department, salary — overrides display()). HOD inherits Professor (adds numFaculty). Main: array of Person pointers with 2 Students, 1 Prof, 1 HOD. Call display() on all. Show constructor chain for HOD object.
Show Solution ▾
#include 
using namespace std;

class Person{
protected: string name; int age;
public:
    Person(string n,int a):name(n),age(a){}
    virtual void display()=0;
    virtual ~Person(){}};
class Student:public Person{
    int rollNo; float gpa; string branch;
public:
    Student(string n,int a,int r,float g,string b):Person(n,a),rollNo(r),gpa(g),branch(b){}
    void display() override{cout<<"Student:"<<name<<" Roll:"<<rollNo<<" GPA:"<<gpa<<"\n";}};
class Professor:public Person{
protected: string dept; double salary;
public:
    Professor(string n,int a,string d,double s):Person(n,a),dept(d),salary(s){}
    void display() override{cout<<"Prof:"<<name<<" Dept:"<<dept<<" Salary:"<<salary<<"\n";}};
class HOD:public Professor{
    int numFaculty;
public:
    HOD(string n,int a,string d,double s,int f):Professor(n,a,d,s),numFaculty(f){}
    void display() override{Professor::display();cout<<"  HOD of "<<dept<<", Faculty:"<<numFaculty<<"\n";}};
int main(){
    Person* uni[]={new Student("Alice",20,101,9.1,"CS"),new Professor("Dr.Smith",45,"Math",80000),new HOD("Dr.Roy",50,"CS",100000,15)};
    if(auto p:uni){p-display();delete p;}
    return 0;
}
Program 10 — Mixed Concept
Inline + Friend + Inheritance Combined
10 marks
Class Temperature (private: celsius). Inline methods: toCelsius(), toFahrenheit(), toKelvin(). Class WeatherStation (private: location, Temperature reading). Friend function printReport(WeatherStation) that accesses private members of BOTH classes and prints all conversions. Show inline inside class body. Demonstrate with 2 WeatherStation objects.
#include 
using namespace std;

class WeatherStation;  // forward declaration
class Temperature{
    double celsius;
public:
    Temperature(double c):celsius(c){}
    inline double toCelsius(){return celsius;}
    inline double toFahrenheit(){return celsius*9/5+32;}
    inline double toKelvin(){return celsius+273.15;}
    friend void printReport(WeatherStation ws);};
class WeatherStation{
    string location;
    Temperature reading;
public:
    WeatherStation(string loc,double c):location(loc),reading(c){}
    friend void printReport(WeatherStation ws);};
void printReport(WeatherStation ws){
    cout<<"Location: "<<ws.location<<"\n";
    cout<<"Celsius: "<<ws.reading.toCelsius()<<"\n";
    cout<<"Fahrenheit: "<<ws.reading.toFahrenheit()<<"\n";
    cout<<"Kelvin: "<<ws.reading.toKelvin()<<"\n";}  
int main(){
    WeatherStation w1("Delhi",38),w2("Arctic",-40);
    printReport(w1);printReport(w2);    return 0;
}

💻

Complete Programs — Full Execution Flow

Every program below is 100% compilable with full headers, main(), and expected output. Study the dry runs to understand memory execution step-by-step.

Program 1 — Multilevel Inheritance: Constructor + Destructor Chain
#include 
using namespace std;

#include <iostream>
using namespace std;

class Vehicle {
protected:
    string brand;
    int speed;
public:
    Vehicle(string b, int s) : brand(b), speed(s) {
        cout << "Vehicle constructor: " << brand << "\n";
    }
    virtual void describe() {
        cout << "Brand: " << brand << ", Speed: " << speed << "\n";
    }
    virtual ~Vehicle() { cout << "Vehicle destructor: " << brand << "\n"; }
};

class Car : public Vehicle {
protected:
    string fuelType;
public:
    Car(string b, int s, string f) : Vehicle(b, s), fuelType(f) {
        cout << "Car constructor: " << fuelType << "\n";
    }
    void describe() override {
        Vehicle::describe();
        cout << "Fuel: " << fuelType << "\n";
    }
    ~Car() { cout << "Car destructor\n"; }
};

class ElectricCar : public Car {
    int batteryRange;
public:
    ElectricCar(string b, int s, int r)
        : Car(b, s, "Electric"), batteryRange(r) {
        cout << "ElectricCar constructor: range=" << batteryRange << "\n";
    }
    void describe() override {
        Car::describe();
        cout << "Battery Range: " << batteryRange << " km\n";
    }
    ~ElectricCar() { cout << "ElectricCar destructor\n"; }
};

int main() {
    cout << "=== Creating ElectricCar ===\n";
    ElectricCar* ec = new ElectricCar("Tesla", 200, 500);
    cout << "\n=== Calling describe() ===\n";
    ec-describe();
    cout << "\n=== Deleting via base pointer ===\n";
    Vehicle* vp = ec;
    delete vp;
    return 0;
}
▶ Expected Output
=== Creating ElectricCar === Vehicle constructor: Tesla Car constructor: Electric ElectricCar constructor: range=500 === Calling describe() === Brand: Tesla, Speed: 200 Fuel: Electric Battery Range: 500 km === Deleting via base pointer === ElectricCar destructor Car destructor Vehicle destructor: Tesla
🧠 Deep Code Explanation
1
ElectricCar* ec = new ElectricCar("Tesla",200,500) — heap allocation begins. Before ElectricCar body runs, it must first call Car("Tesla",200,"Electric").
2
Car constructor calls Vehicle("Tesla",200) first via initializer list. Vehicle ctor runs → prints "Vehicle constructor: Tesla".
3
Back to Car ctor body → prints "Car constructor: Electric". Then ElectricCar body → prints range=500.
4
ec-describe() — virtual dispatch: actual object is ElectricCar, so ElectricCar::describe() runs. It calls Car::describe() which calls Vehicle::describe() — chaining upward.
5
delete vp where vp is Vehicle* pointing to ElectricCar. Because ~Vehicle() is virtual → runtime checks VTable → calls ~ElectricCar() FIRST, then ~Car(), then ~Vehicle(). Correct cleanup order!
✍️ How to Write This in Exam
1
Write the deepest derived class at the top of your planning. Work backward to find what base classes it needs.
2
Every constructor uses initializer list to pass args to parent: Derived(args) : Base(base_args) {}
3
If polymorphism needed (calling via base pointer): mark methods virtual in base, override in derived, virtual destructor in base.
4
Always test: create object → call method via base pointer → delete via base pointer. These 3 lines test your full hierarchy.
Common mistake: Missing virtual destructor → only base destructor called → derived memory leaked.
▶ Dry Run
new ElectricCar(...)Stack: [vp, ec] | Heap: [Vehicle part | Car part | ElectricCar part]
ctor orderVehicle("Tesla",200) → Car(..."Electric") → ElectricCar(range=500)
ec->describe()VTable lookup: ElectricCar* → ElectricCar::describe → calls Car::describe → calls Vehicle::describe
Vehicle* vp = ecNo copy. vp just holds same address as ec. Type of pointer = Vehicle*
delete vpVTable: ~Vehicle is virtual → dispatches to ~ElectricCar → ~Car → ~Vehicle
Stack after deleteec, vp both dangling pointers (heap freed). Program exits cleanly.

Program 2 — Abstract Class + Runtime Polymorphism (Must-Write Pattern)
#include 
using namespace std;

#include <iostream>
#include <cmath>
using namespace std;

class Shape {                          // Abstract base class
public:
    string color;
    Shape(string c) : color(c) {}
    virtual double area()      = 0;    // pure virtual
    virtual double perimeter() = 0;    // pure virtual
    virtual void display() {
        cout << "[" << color << "] ";
    }
    virtual ~Shape() {}
};

class Circle : public Shape {
    double radius;
public:
    Circle(string c, double r) : Shape(c), radius(r) {}
    double area()      override { return 3.14159 * radius * radius; }
    double perimeter() override { return 2 * 3.14159 * radius; }
    void display() override {
        Shape::display();
        cout << "Circle r=" << radius
             << " | Area=" << area()
             << " | Perimeter=" << perimeter() << "\n";
    }
};

class Rectangle : public Shape {
    double width, height;
public:
    Rectangle(string c, double w, double h)
        : Shape(c), width(w), height(h) {}
    double area()      override { return width * height; }
    double perimeter() override { return 2 * (width + height); }
    void display() override {
        Shape::display();
        cout << "Rect " << width << "x" << height
             << " | Area=" << area()
             << " | Perimeter=" << perimeter() << "\n";
    }
};

class Triangle : public Shape {
    double a, b, c;
public:
    Triangle(string col, double a, double b, double c)
        : Shape(col), a(a), b(b), c(c) {}
    double perimeter() override { return a + b + c; }
    double area() override {
        double s = perimeter() / 2;           // Heron's formula
        return sqrt(s*(s-a)*(s-b)*(s-c));
    }
    void display() override {
        Shape::display();
        cout << "Triangle " << a << "," << b << "," << c
             << " | Area=" << area() << "\n";
    }
};

int main() {
    Shape* shapes[] = {
        new Circle("Red", 5),
        new Rectangle("Blue", 4, 6),
        new Triangle("Green", 3, 4, 5)
    };

    double totalArea = 0;
    cout << "=== Shape Report ===\n";
    if(Shape* s : shapes) {
        s-display();
        totalArea += s-area();
    }
    cout << "Total Area: " << totalArea << "\n";

    if(Shape* s : shapes) delete s;
    return 0;
}
▶ Expected Output
=== Shape Report === [Red] Circle r=5 | Area=78.5397 | Perimeter=31.4159 [Blue] Rect 4x6 | Area=24 | Perimeter=20 [Green] Triangle 3,4,5 | Area=6 | Perimeter=12 Total Area: 108.54
🧠 Deep Code Explanation
1
Shape* shapes[] = {...} — array of BASE CLASS POINTERS. Each pointer holds address of a DIFFERENT derived object on heap. This is the foundation of polymorphism.
2
if(Shape* s : shapes) s-display() — same code, different behavior for each. Virtual dispatch checks VTable at runtime. Circle → Circle::display, Rectangle → Rectangle::display, Triangle → Triangle::display.
3
totalArea += s-area() — area() is pure virtual in Shape. Each derived class provides its own formula. The BASE class code calls the DERIVED formula — this is runtime polymorphism in action.
4
Triangle area uses Heron's formula: s = semi-perimeter = (3+4+5)/2 = 6. area = √(6×3×2×1) = √36 = 6.0 ✓ (3-4-5 right triangle)
5
if(Shape* s:shapes) delete s — virtual destructor ensures each derived destructor runs, preventing memory leaks.
✍️ How to Write This in Exam
1
Start: Write abstract base class with all pure virtual functions + virtual destructor.
2
For each derived: inherit publicly, override ALL pure virtuals (otherwise derived is also abstract).
3
main(): Create base pointer array, fill with new DerivedClass(), loop and call virtual methods, delete at end.
4
Pattern to memorize: BaseClass* arr[] = {new D1(), new D2()}; if(auto p:arr){p-method(); delete p;}
▶ Dry Run
shapes[0]Circle at addr 0x100. vptr → Circle VTable [area=Circle::area, perimeter=Circle::perimeter, display=Circle::display]
shapes[1]Rectangle at 0x110. vptr → Rectangle VTable
s->display() i=0Compiler: s is Shape* → look at vptr → Circle VTable → Circle::display(). color="Red", radius=5
area() in display()Calls this->area() → virtual → Circle::area() = 3.14159×5×5 = 78.54
totalArea78.54 + 24 + 6 = 108.54
delete shapes[0]~Shape() virtual → ~Circle() → object freed

Program 3 — Friend Function, Inline & Multiple Classes
#include 
using namespace std;

#include <iostream>
using namespace std;

class Celsius;  // Forward declaration

class Fahrenheit {
    double fahr;
public:
    Fahrenheit(double f) : fahr(f) {}
    inline double getFahr() const { return fahr; }
    friend void printComparison(Fahrenheit f, Celsius c);
};

class Celsius {
    double cel;
public:
    Celsius(double c) : cel(c) {}
    inline double getCel() const { return cel; }
    inline double toFahrenheit() const { return cel * 9.0/5.0 + 32; }
    friend void printComparison(Fahrenheit f, Celsius c);
};

// Friend function — accesses BOTH classes' private members
void printComparison(Fahrenheit f, Celsius c) {
    cout << "Fahrenheit value  : " << f.fahr << "°F\n";
    cout << "Celsius value     : " << c.cel  << "°C\n";
    cout << "Celsius as °F     : " << c.toFahrenheit() << "°F\n";
    cout << "Are they equal?   : "
         << (f.fahr == c.toFahrenheit() ? "YES" : "NO") << "\n";
}

int main() {
    Fahrenheit boiling(212.0);
    Celsius    water(100.0);
    printComparison(boiling, water);

    cout << "\n";
    Fahrenheit freezing(32.0);
    Celsius    ice(0.0);
    printComparison(freezing, ice);
    return 0;
}
▶ Expected Output
Fahrenheit value : 212°F Celsius value : 100°C Celsius as °F : 212°F Are they equal? : YES Fahrenheit value : 32°F Celsius value : 0°C Celsius as °F : 32°F Are they equal? : YES
🧠 Deep Code Explanation
1
class Celsius; — Forward declaration. Fahrenheit needs to mention Celsius in friend declaration, but Celsius isn't fully defined yet. Forward declaration tells the compiler "this class exists, full definition comes later."
2
friend void printComparison(Fahrenheit, Celsius) declared INSIDE BOTH classes. Both grant access to their private members to this single function.
3
Inside printComparison: f.fahr accesses Fahrenheit's private member directly (NOT via getter). Same for c.cel. This is the ONLY place outside these classes where this is possible.
4
inline double toFahrenheit() — body is inside class definition, so it's automatically inline. No function call overhead.
✍️ How to Write This in Exam
1
When asked for friend across classes: always write forward declaration of class that appears in the parameter list but not yet defined.
2
Declare friend in BOTH classes if the function needs both. Write function body OUTSIDE both classes (it's not a member of either).
3
Inline: either write body inside class, or use keyword inline outside. Functions inside class body are implicitly inline.

🚀 Live C++ Compiler