Skip to main content

Chapter 4b - Inheritance & Polymorphism

Inheritance​

Inheritance lets a derived class (child) reuse and extend the behavior of a base class (parent). The child gets all public and protected members of the parent automatically.

// Base class
class Animal {
public:
string name;
int age;

Animal(string n, int a) : name(n), age(a) {}

void eat() {
cout << name << " is eating." << endl;
}

void sleep() {
cout << name << " is sleeping." << endl;
}
};

// Derived class
class Dog : public Animal {
public:
string breed;

Dog(string n, int a, string b) : Animal(n, a), breed(b) {}

void bark() {
cout << name << " says: Woof!" << endl;
}
};
int main() {
Dog rex("Rex", 3, "Labrador");
rex.eat(); // inherited from Animal
rex.sleep(); // inherited from Animal
rex.bark(); // defined in Dog
}

Access in Inheritance​

Member in Basepublic inheritanceprotected inheritanceprivate inheritance
publicpublic in childprotected in childprivate in child
protectedprotected in childprotected in childprivate in child
privatenot accessiblenot accessiblenot accessible

Use public inheritance almost always. The others are rare.

πŸ§ͺ Inheritance

Calling the Parent Constructor​

The derived class constructor must forward arguments to the base class constructor using the initializer list.

class Cat : public Animal {
public:
bool isIndoor;

Cat(string n, int a, bool indoor)
: Animal(n, a), // call base constructor first
isIndoor(indoor)
{}

void describe() {
cout << name << " (age " << age << ") β€” "
<< (isIndoor ? "indoor" : "outdoor") << " cat." << endl;
}
};

Mini quiz β€” inheritance setup​

Quiz

Inheritance

1 / 3

This means Dog…

class Dog : public Animal {

Pick an answer to see if you are right.


Method Overriding​

A derived class can override a base class method by redefining it with the same signature.

class Animal {
public:
string name;
Animal(string n) : name(n) {}

void speak() {
cout << name << " makes a sound." << endl;
}
};

class Dog : public Animal {
public:
Dog(string n) : Animal(n) {}

void speak() { // overrides Animal::speak
cout << name << " says: Woof!" << endl;
}
};

class Cat : public Animal {
public:
Cat(string n) : Animal(n) {}

void speak() {
cout << name << " says: Meow!" << endl;
}
};

Virtual Functions & Polymorphism​

Without virtual, C++ decides which method to call based on the pointer type (static dispatch). With virtual, it decides based on the actual object type at runtime (dynamic dispatch).

class Animal {
public:
string name;
Animal(string n) : name(n) {}

virtual void speak() { // <-- virtual keyword
cout << name << " makes a sound." << endl;
}
};

class Dog : public Animal {
public:
Dog(string n) : Animal(n) {}
void speak() override { // <-- override keyword (C++11)
cout << name << " says: Woof!" << endl;
}
};

class Cat : public Animal {
public:
Cat(string n) : Animal(n) {}
void speak() override {
cout << name << " says: Meow!" << endl;
}
};

int main() {
Animal* animals[3];
animals[0] = new Dog("Rex");
animals[1] = new Cat("Whiskers");
animals[2] = new Dog("Buddy");

for (int i = 0; i < 3; i++) {
animals[i]->speak(); // calls the right version for each object
}

for (int i = 0; i < 3; i++) delete animals[i];
}
Rex says: Woof!
Whiskers says: Meow!
Buddy says: Woof!
override keyword

override (C++11) tells the compiler "this method must override a virtual method in the base." If you mistype the name, the compiler will catch it. Always use override.

πŸ§ͺ Polymorphism

Challenge: triangle area​

Finish triangleArea for a right triangle with legs a and b (area = a * b / 2).

abReturn
346
6824

Virtual Destructors​

If you ever delete a derived object through a base pointer, the base class destructor must be virtual, otherwise only the base destructor runs and the derived part leaks.

class Base {
public:
virtual ~Base() { cout << "Base destroyed." << endl; }
};

class Derived : public Base {
public:
~Derived() override { cout << "Derived destroyed." << endl; }
};

int main() {
Base* obj = new Derived();
delete obj; // Derived destroyed. then Base destroyed. (correct order)
}
Rule of thumb

If a class has any virtual function, give it a virtual ~Destructor().


Abstract Classes & Pure Virtual Functions​

A pure virtual function has no implementation in the base class and forces derived classes to provide one. A class with at least one pure virtual function is abstract β€” you cannot instantiate it directly.

class Shape {
public:
string color;
Shape(string c) : color(c) {}

virtual double area() = 0; // pure virtual
virtual double perimeter() = 0; // pure virtual

void describe() {
cout << color << " shape | area=" << area()
<< " perimeter=" << perimeter() << endl;
}
};
red shape | area=78.5397 perimeter=31.4159
blue shape | area=24 perimeter=20
πŸ§ͺ Abstract Shape

Mini quiz β€” virtual and abstract​

Quiz

Virtual destructors and abstract classes

1 / 3

If you delete a derived object through a base pointer, the base destructor should be…

Pick an answer to see if you are right.


The Inheritance Hierarchy​

Shape (abstract)
/ \
Circle Rectangle
|
Square (Rectangle with w == h)
class Square : public Rectangle {
public:
Square(string c, double side) : Rectangle(c, side, side) {}
};

Multiple Inheritance​

C++ allows a class to inherit from more than one base class. Use carefully β€” it can lead to the "diamond problem."

class Flyable {
public:
virtual void fly() { cout << "Flying!" << endl; }
};

class Swimmable {
public:
virtual void swim() { cout << "Swimming!" << endl; }
};

class Duck : public Flyable, public Swimmable {
public:
void quack() { cout << "Quack!" << endl; }
};
πŸ§ͺ Multiple inheritance

Mini quiz β€” hierarchy and multiple inheritance​

Quiz

Hierarchies

1 / 3

In a typical shape tree, Circle is…

Pick an answer to see if you are right.


Exercises​

Activity: Shape Hierarchy

Extend the Shape hierarchy with a Triangle class. Add a method isLarger(Shape* other) to the base class that compares areas.

πŸ“’ Solution