Inheritance & Polymorphism
ইনহেরিট্যান্স ও পলিমর্ফিজম
1. The "is-a" Relationship
Inheritance models "is-a". A Dog is-a Animal. Inherit when the derived type can substitute for the base.
#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
| Inheritance | Base public → | Base protected → |
|---|---|---|
: public Base | public | protected |
: protected Base | protected | protected |
: private Base | private | private |
3. Constructor Chaining
The base class is constructed first. Pass args via member init list:
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:
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
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
- Define
Vehiclebase class andCarderived class.✨ Show Answer
class Vehicle { public: int wheels; }; class Car : public Vehicle { public: Car() { wheels = 4; } }; - 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.
- 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.
- 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.
- What does
protectedmean?✨ Show Answer
Visible to the class itself and its subclasses, but not the outside world. Use sparingly; it weakens encapsulation.
- Define a
Shapebase witharea()and aSquareoverride.✨ 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.)
- 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.
- 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.
- What does
finalon a class mean?✨ Show Answer
class X final { ... }— the class cannot be inherited from. - 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).
- Why should base destructors usually be
virtual?✨ Show Answer
Without virtual,
delete basePtronly calls the base destructor — derived destructor and resources leak. (Detail in Module 23.) - 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. - 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.
- A
Penguinis aBird. 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).
- Add a virtual function
speak()to Animal.✨ Show Answer
class Animal { public: virtual void speak() const { std::cout << "...\n"; } virtual ~Animal() = default; }; - 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.