This project has been created as part of the 42 curriculum by baelgadi.

CPP Module 03

Inheritance


The ring chooses you, but the oath came before you.
Every Lantern in the Corps inherits the same light.

A class that inherits is a Lantern accepting the ring, and with it comes every power of the one before it and… every duty too.


Previously…


CPP Module 03:

4 exercises. C++98 standard only.
Compiler: c++ · Flags: -Wall -Wextra -Werror · Must compile with -std=c++98

Exercise 00: Aaaaand… Open! · ex00/ · ClapTrap, the base ring
Exercise 01: Serena, my love! · ex01/ · ScavTrap inherits from ClapTrap
Exercise 02: Repetitive work · ex02/ · FragTrap inherits from ClapTrap
Exercise 03: Now it’s weird! · ex03/ · DiamondTrap inherits from both

Forbidden: using namespace (-42) · friend (-42) · printf · *alloc · free · STL containers · STL algorithms · function bodies in headers
Required: Orthodox Canonical Form on every class · every constructor and destructor must print

Inheritance

The Corps passes the ring down.
Each new Lantern wakes up already knowing how to fly, already knowing how to bind a construct.

class ClapTrap {
protected:
    std::string name;
    int hitPoints;
    int energyPoints;
    int attackDamage;
};

class ScavTrap : public ClapTrap { /* gets everything above for free */ };

ScavTrap does not redeclare hitPoints. It already has one (the one inherited from ClapTrap).
Redeclaring would be forging a second ring for the same finger.

The hierarchy:

[!NOTE] Always use public inheritance for CPP03.
private inheritance would lock the parent’s public members away from anyone outside the child.


Access specifiers

The Guardians of Oa keep some knowledge for themselves, some for the Corps, and some for everyone in the galaxy.

Specifier Who can touch it meaning…
public Everyone civilians, allies, enemies
protected The Corps this class and its descendants
private The Guardians alone this class and nothing else
// In ClapTrap.hpp
protected:
    std::string name;
    int         hitPoints;
    int         energyPoints;
    int         attackDamage;

If hitPoints stayed private, ScavTrap’s constructor could not write this->hitPoints = 100.

[!WARNING] A child class does NOT redeclare the parent’s attributes.
There is one hitPoints, inherited.
Declaring it again in ScavTrap creates a second, separate field.


Orthodox Canonical Form, a derived twist

CPP02 taught the 4: default constructor, copy constructor, copy assignment, destructor.
CPP03 adds one rule on top.

When you copy a child, you must copy the parent’s part too.

// Copy constructor
ScavTrap::ScavTrap(ScavTrap const &src) : ClapTrap(src)
{
    // ClapTrap(src) copies the inherited fields
    // anything ScavTrap-specific would be copied here
}

// Copy assignment: same idea
ScavTrap &ScavTrap::operator=(ScavTrap const &rhs)
{
    if (this != &rhs)
        ClapTrap::operator=(rhs);
    return *this;
}

Forget ClapTrap(src) and the base portion gets default-constructed instead of copied!
The new Lantern wakes up with a blank ring while you thought you were cloning a veteran.


Construction and Destruction Order

The oath is recited from the base up. Otherwise the ring will not power on.

ScavTrap::ScavTrap(std::string const &name) : ClapTrap(name)
{
    this->hitPoints    = 100;
    this->energyPoints = 50;
    this->attackDamage = 20;
    std::cout << "ScavTrap " << name << " constructed" << std::endl;
}

The : ClapTrap(name) is the initializer list. It runs before the body.
ClapTrap is built, prints its message, then ScavTrap takes over.

For ScavTrap s("GuyGardner"):

1. ClapTrap("GuyGardner") constructed       ← base first
2. ScavTrap("GuyGardner") constructed       ← derived after
   ...
3. ScavTrap "GuyGardner" destroyed          ← derived first
4. ClapTrap "GuyGardner" destroyed          ← base last
Phase Order
Construction Deepest parent → most derived
Destruction Most derived → deepest parent

The Diamond Problem and Virtual Inheritance

Parallax is what happens when one being is split in two.
The diamond problem is the same idea in code.

Without virtual, DiamondTrap contains 2 ClapTrap subobjects (one from each parent).
this->hitPoints becomes ambiguous.
Which ClapTrap? Which ring? The compiler refuses.

The fix is willpower made into a keyword.

class ScavTrap : virtual public ClapTrap {  };
class FragTrap : virtual public ClapTrap {  };
class DiamondTrap : public ScavTrap, public FragTrap {  };

virtual public ClapTrap tells the compiler: there is only ever one ClapTrap inside any descendant, no matter how many paths lead to it.

The virtual base rule

When a virtual base is involved, the most derived class initializes it directly.
Intermediate classes calls to that base are ignored.

DiamondTrap::DiamondTrap(std::string const &name)
    : ClapTrap(name + "_clap_name"),    // DiamondTrap calls ClapTrap directly
    ScavTrap(name),                     // the ClapTrap(name) call from here is SKIPPED
    FragTrap(name),                     // the ClapTrap(name) call from here is SKIPPED
    name(name)                          // DiamondTrap's own private name
{
    this->hitPoints = 100;      // from FragTrap
    this->energyPoints = 50;    // from ScavTrap value
    this->attackDamage = 30;    // from FragTrap
    std::cout << "DiamondTrap " << name << " constructed" << std::endl;
}

[!WARNING] ScavTrap sets EP=50. FragTrap runs after and overwrites EP=100.
The subject demands EP=50 so we have to manually correct it in DiamondTrap’s body.

Construction order for DiamondTrap:

1. ClapTrap("Diamond_clap_name")    ← virtual base, called directly
2. ScavTrap("Diamond")              ← its ClapTrap() ignored
3. FragTrap("Diamond")              ← its ClapTrap() ignored
4. DiamondTrap("Diamond")           ← corrects EP back to 50

Name Shadowing

Hal Jordan and Green Lantern of 2814 are the same man. Both names exist, both are real.
Scope tells you which one is being called.

class ClapTrap {
protected:
    std::string name;   // ClapTrap::name → "Diamond_clap_name"
};

class DiamondTrap : public ScavTrap, public FragTrap {
private:
    std::string name;   // DiamondTrap::name → "Diamond"
};

The subject demands this intentional shadowing.
It even hints at the -Wshadow flag, not as a warning to fix, but as proof you know what you are doing.

void DiamondTrap::whoAmI(void)
{
    std::cout << "DiamondTrap name: " << this->name  << std::endl;
    std::cout << "ClapTrap name: " << ClapTrap::name  << std::endl;
}

For ClapTrap::name to reach DiamondTrap, ClapTrap’s name must be protected. Another reason private would have broken the module.

[!NOTE] Since we are compiling with c++ the -Wshadow doesn’t warn on derived members shadows base member case.
clang++ has -Wshadow-all for this.


Instructions

# ──── ex00 ────
cd ex00 && make
./claptrap

# ──── ex01 ────
# Verify: "ClapTrap X constructed" before "ScavTrap X constructed"
# Verify: ScavTrap attack message != ClapTrap attack message
cd ex01 && make
./scavtrap

# ──── ex02 ────
# Same chain check, but for FragTrap
# Verify: highFivesGuys() prints a message
cd ex02 && make
./fragtrap

# ──── ex03 ────
# Verify: ClapTrap → ScavTrap → FragTrap → DiamondTrap on construction
# Verify: DiamondTrap → FragTrap → ScavTrap → ClapTrap on destruction
# Verify: whoAmI() shows both "Diamond" and "Diamond_clap_name"
# Verify: 50 energy points
cd ex03 && make
./diamondtrap

Attribute verification for DiamondTrap:

DiamondTrap d("D");
for (int i = 0; i < 50; i++)
    d.attack("x");
d.attack("x");   // must print "no energy"

Resources


You can inherit the ring. You can stand in the Corps line and look the part.
But the diamond is what tells the Guardians whether you understood any of it.

In brightest day, in blackest night, no ClapTrap shall be constructed twice.