Lecture 13
Base and Derived Classes
Inheritance lets one class (the derived class) extend another (the base class). The derived class automatically gets the base class's public and protected members, and can add its own.
Dog inherits name() and name_ from Animal. It adds bark() on its own.
protected Access
protected members are visible to the class itself and to its derived classes, but not to
outside code:
Constructor Chaining
A derived class constructor must initialize the base part of the object. It does this by calling the base class constructor in the member initializer list:
Base constructors always run first, before any of the derived class's member initializers.
Method Hiding vs Overriding
A derived class can define a method with the same name as one in the base class. Without virtual,
the call is resolved at compile time based on the static type of the pointer or reference —
this is hiding, not overriding:
The compiler sees Animal* and calls Animal::speak, ignoring that a actually points to a
Cat. To get dynamic dispatch — the call going to the right type at runtime — you need
virtual.
Virtual Functions
Marking a method virtual in the base class tells the compiler to dispatch based on the actual
runtime type of the object, not the static pointer type:
The compiler stores a hidden vtable (virtual function table) in each polymorphic class. A virtual call looks up the right function pointer at runtime.
The override Keyword
override tells the compiler "this is intentionally overriding a base class virtual." If the
signature doesn't match any base virtual, it's a compile error:
Always write override on derived methods. It catches typos and signature mismatches that would
otherwise silently become hiding instead of overriding.
Abstract Classes and Pure Virtual Functions
A pure virtual function has no definition in the base class and forces derived classes to provide one:
A class with at least one pure virtual function is abstract — it cannot be instantiated directly, only through derived classes that implement all pure virtuals.
Virtual Destructors
When deleting a derived object through a base pointer, the compiler calls the base destructor — unless it is virtual:
Marking the base destructor virtual fixes this:
Any class intended to be used polymorphically — with derived objects held through base pointers — must have a virtual destructor.
Extending String — PrefixedString
We now add a virtual display() method to String and introduce PrefixedString, a derived
class that prepends a label when displaying.
First, add to String:
Making the destructor virtual means the compiler no longer generates a default one. We must define it explicitly.
PrefixedString stores a prefix using our own String type and overrides display():
Because prefix_ is a String object (not a raw pointer), its copy/move/destroy operations are
handled automatically by String's existing special members. PrefixedString needs no destructor
of its own.
Exercises
Q1.
Without running the code, predict what this program prints.
Q2.
Without running the code, predict what this program prints.
Q3.
Each snippet has a bug related to inheritance. Identify what's wrong.
Q4.
Extend the Animal hierarchy: add a Cat class and a Parrot class that override speak().
Then write a function make_noise(Animal** animals, int n) that calls speak() on each.
Q5.
Add a virtual void display() const method to String that prints the contents followed by a
newline. Then write a PrefixedString class that inherits from String, stores a String prefix,
and overrides display() to print <prefix><contents>\n.
Practice Problems
- Design Browser History — Medium
- Design Underground System — Medium