Chapter 12 · Week 12

Inheritance in C++

"Every scribe who has been trained for the kingdom of heaven is like a master of a house, who brings out of his treasure what is new and what is old." — Matthew 13:52
12

Polymorphic dispatch in action

Four Argument* pointers, four concrete subclasses, one call. Watch the right override fire each time. This is the move that distinguishes Phase 2 from Phase 1.

Polymorphic Dispatch — The Argument Case File

A CaseFile holds four Argument* pointers — but each one actually points to a different concrete subclass. Click defend() on each row. The same call, dispatched to the right override. That's polymorphism.

Argument*[4] static type → Argument* · dynamic type varies per slot
Polymorphic dispatch — what each pointer ran
Click defend() on one of the arguments above to watch dispatch.
The classical apologetic case is plural, not singular. No single argument settles the question of God. The case is several arguments working together — each strong against different objections, each covering the others' weaknesses. Polymorphism is the code shape of that move.

Why This Matters

You've built classes that work in isolation. Now we build classes that share structure.

Inheritance lets one class build on another. A Car is a Vehicle, so you write Vehicle once with the shared parts, and Car inherits and adds only what's car-specific.

Polymorphism is the payoff: if you have a Vehicle* that's actually pointing at a Car, and you call vehicle->describe(), the right describe runs — Car's, not Vehicle's. The runtime dispatches based on what the object actually is. You can put a mixed array in Vehicle*[] and iterate, and each reports itself correctly.

The Syntax

class Argument {
protected:
    string label, claim;
public:
    Argument(string l, string c) : label(l), claim(c) {}
    virtual ~Argument() {}          // ← virtual destructor
    virtual void defend() const {          // ← virtual method
        cout << "[" << label << "] " << claim << endl;
    }
};

class Cosmological : public Argument {
public:
    Cosmological() : Argument("Cosmological", "...") {}
    void defend() const override {        // ← override
        // Cosmological-specific case
    }
};

Key vocabulary:

  • : public Argument — Cosmological inherits from Argument.
  • protected — visible to subclasses, invisible outside.
  • virtual — this method can be overridden; dispatch is dynamic.
  • override — affirms the derived method overrides a virtual. Compiler will reject typos.

Always Make Destructors Virtual in Polymorphic Hierarchies

Argument* a = new Cosmological();
delete a;

If ~Argument is not virtual, the compiler only calls ~Argument on delete — not ~Cosmological. The Cosmological-specific cleanup is skipped. Virtual destructor or memory leaks.

Pure Virtual = Abstract

class Argument {
public:
    virtual void defend() const = 0;   // pure virtual — no body
};

A class with any pure virtual is abstract. You cannot instantiate it directly. Every concrete subclass must implement defend or it too becomes abstract. This forces the contract.

Slicing — A Common Bug

void inspect(Argument a)  {     // ⚠ pass by value
    a.defend();
}
Cosmological c;
inspect(c);   // SLICED — the Cosmological parts are gone

Pass by value copies a base-sized chunk; the derived parts are sliced off. To keep polymorphism alive: pass by pointer or reference (Argument* or const Argument&), never by value.

Coach's Note — No single argument is "the" argument for God. The classical case is several arguments working together — each strong against different objections, each shoring up the others. That's exactly what polymorphism over a case file is doing in code. The widget above is the case file in motion.

This Week's Project

You're ready for Project 12: Argument Case File. Build an abstract Argument base with three concrete subclasses (Cosmological, Moral, Ontological). A CaseFile stores them as Argument* and iterates polymorphically. Real attributions required — every defend() must cite a real source you can verify.

Check Your Reps

Inheritance & Polymorphism — Quick Check

Question 1 of 4
You have Argument* a = new Cosmological(); and call a->defend(). Which version runs?
Why: If defend is marked virtual in Argument, dispatch happens at runtime based on the dynamic type. a actually points to a Cosmological, so Cosmological::defend() fires. Without the virtual keyword, the base version would run — the classic missing-virtual bug.
Question 2 of 4
Why is this a memory leak?
class Argument {
public:
    ~Argument() {}   // not virtual
};

Argument* a = new Cosmological();
delete a;
Why: Without a virtual destructor, delete a only invokes ~Argument(). Cosmological's destructor never fires; if Cosmological holds any resources (like a vector of premises) they leak. Rule: any class with virtual methods must have a virtual destructor.
Question 3 of 4
What happens here?
void inspect(Argument a) {   // by value
    a.defend();
}
Cosmological c;
inspect(c);
Why: Pass-by-value copies the Argument-sized portion of c; the Cosmological-specific fields are sliced off. Inside inspect, a is a pure Argument. Fix: change the parameter to const Argument& or Argument*.
Question 4 of 4
What does it mean for the project that "no single argument is the argument for God"?
Why: The Coach's Note on Project 12 makes this explicit. Polymorphism is the code shape of "several arguments working together" — a CaseFile holding multiple Argument* entries, each defending characteristically. A case file with only one Argument* would defeat the pedagogical point.
YOU FINISHED. NICE WORK.

← WEEK 11: POINTERS   ·   WEEK 13: HELLO JAVA →