Inheritance & Polymorphism

ইনহেরিট্যান্স ও পলিমর্ফিজম

Read: ~35 min 16 practice problems

1. The "is-a" Relationship

Inheritance models "is-a". A Dog is-a Animal. Inherit when the derived type can substitute for the base.

inherit.cpp
#include <iostream>
#include <string>

class Animal {
protected:
    std::string name_;
public:
    Animal(std::string n) : name_(std::move(n)) {}
    void sleep() const { std::cout << name_ << " is sleeping\n"; }
};

class Dog : public Animal {
public:
    Dog(std::string n) : Animal(std::move(n)) {}
    void bark() const { std::cout << name_ << ": Woof!\n"; }
};

int main() {
    Dog d{"Rex"};
    d.sleep();   // inherited
    d.bark();    // own
}

2. Access Modes for Inheritance

InheritanceBase public →Base protected →
: public Basepublicprotected
: protected Baseprotectedprotected
: private Baseprivateprivate
Use public inheritance for "is-a". Private/protected are rare and almost always worse than composition (has-a).

3. Constructor Chaining

The base class is constructed first. Pass args via member init list:

chain.cpp
class Base {
public:
    Base(int x) { std::cout << "Base(" << x << ")\n"; }
};

class Derived : public Base {
public:
    Derived(int x, int y) : Base(x) {
        std::cout << "Derived(" << y << ")\n";
    }
};
// Derived d{1, 2};   prints Base(1) then Derived(2)

4. Slicing — A Gotcha

If you assign a derived object to a base value (not pointer/reference), the derived part is sliced off:

slice.cpp
Dog rex{"Rex"};
Animal a = rex;     // SLICED: only Animal part stays
a.bark();           // ERROR: Animal has no bark()

// Always use a base reference or pointer:
Animal& ar = rex;
// ar.bark();       // still error w/o virtual; see Module 23
The slicing problem Storing polymorphic objects by value loses derived data. Always work through pointers or references for polymorphism.

5. Composition vs Inheritance

Prefer composition (has-a) over inheritance (is-a) when possible. A Car has-a Engine; it isn't an Engine.

Composition (has-a)

  • Loose coupling
  • Runtime swappable
  • Easy to test

Inheritance (is-a)

  • True subtype relationship
  • Compile-time bound
  • Use when polymorphic

6. Practice Problems

  1. Define Vehicle base class and Car derived class.
    ✨ Show Answer
    class Vehicle { public: int wheels; };
    class Car : public Vehicle {
    public:
        Car() { wheels = 4; }
    };
  2. In Derived d;, what runs first — Base ctor or Derived ctor body?
    ✨ Show Answer

    Base ctor first. Then Derived's member init list. Then Derived's body.

  3. What is "slicing"?
    ✨ Show Answer

    When assigning a derived object to a base value (not pointer/ref), the derived part is dropped. The result is a pure base, missing all derived state and overrides.

  4. Why prefer public inheritance to private?
    ✨ Show Answer

    Public expresses "is-a" — what people expect. Private inheritance simulates "implemented in terms of", but composition is usually clearer.

  5. What does protected mean?
    ✨ Show Answer

    Visible to the class itself and its subclasses, but not the outside world. Use sparingly; it weakens encapsulation.

  6. Define a Shape base with area() and a Square override.
    ✨ Show Answer
    class Shape { public: virtual double area() const = 0; };
    class Square : public Shape {
        double s_;
    public:
        Square(double s) : s_(s) {}
        double area() const override { return s_*s_; }
    };

    (Module 23 covers virtual / override.)

  7. When is composition better than inheritance?
    ✨ Show Answer

    When the relationship is "has-a", when behavior should be swappable at runtime, or when you only need part of the base's interface. Inheritance binds you tightly to the base.

  8. Pass a Dog to a function taking const Animal& — does it work?
    ✨ Show Answer

    Yes. A Dog is-a Animal; the reference binds. No slicing because it's a reference.

  9. What does final on a class mean?
    ✨ Show Answer

    class X final { ... } — the class cannot be inherited from.

  10. Multiple inheritance — pros and cons?
    ✨ Show Answer

    Pros: combine multiple interfaces (good for mixins). Cons: diamond problem, ambiguity, complexity. Most modern designs avoid it; use only for "interface" classes (all virtual, no state).

  11. Why should base destructors usually be virtual?
    ✨ Show Answer

    Without virtual, delete basePtr only calls the base destructor — derived destructor and resources leak. (Detail in Module 23.)

  12. Show constructor order with two-level inheritance.
    ✨ Show Answer

    A → B → C. class C : public B, class B : public A. Constructing a C runs A's ctor, then B's, then C's.

  13. Why might inheritance be "the hammer of the OOP novice"?
    ✨ Show Answer

    It's overused. People reach for inheritance when composition or templates would be cleaner, ending up with deep hierarchies that are hard to maintain.

  14. A Penguin is a Bird. Should Penguin inherit Bird::fly()?
    ✨ Show Answer

    This is the classic LSP example. If Bird::fly is in the interface, Penguin breaks the "is-a" contract. Better: split FlyingBird and FlightlessBird, or use composition (Bird has-a flight strategy).

  15. Add a virtual function speak() to Animal.
    ✨ Show Answer
    class Animal {
    public:
        virtual void speak() const { std::cout << "...\n"; }
        virtual ~Animal() = default;
    };
  16. Override speak() in Dog.
    ✨ Show Answer
    class Dog : public Animal {
    public:
        void speak() const override { std::cout << "Woof\n"; }
    };

Summary

Inheritance models "is-a". Use public inheritance. Constructors chain base → derived. Beware slicing when assigning derived to base by value. Prefer composition unless you really mean is-a. The next module shows real polymorphism with virtual functions.

Next Module → Virtual Functions, Abstract Classes & Interfaces.