✅ Status: Completed – all exercises
🏫 School: 42 – C++ Modules (Module 03)
🏅 Score: 100/100
Single inheritance, multiple inheritance, method overriding, base/derived behavior, and the classic “diamond” problem.
This repository contains my solutions to 42’s C++ Module 03 (C++98).
The module focuses on inheritance and how base/derived classes interact:
- Single inheritance (
ClapTrap→ScavTrap/FragTrap) - Overriding behavior (e.g., redefining
attack()in derived classes) - Understanding constructor/destructor order
- Multiple inheritance and the diamond problem (
DiamondTrap) - Sharing a single base with virtual inheritance
All exercises are written in C++98 and compiled with strict flags (-Wall -Wextra -Werror -std=c++98).
Concepts practiced in this module:
- Inheritance (
publicinheritance for “is-a”) - Overriding methods and calling base implementations when needed
- Constructor initialization lists in an inheritance chain
- Rule of Three (copy constructor, assignment operator, destructor)
- Multiple inheritance and resolving ambiguity
- Virtual inheritance to solve the “diamond” (single shared base)
A small combat-style class used as a base for the rest of the module.
Goal:
Implement ClapTrap with the canonical behavior:
attack(target)consumes energy and prints an actiontakeDamage(amount)decreases hit pointsbeRepaired(amount)consumes energy and restores hit points
Concepts practiced:
- Basic class design
- State management (HP/EP/AD)
- Copy / assignment / destructor (Rule of Three)
First derived class: specialized behavior + custom stats.
Goal:
Create ScavTrap inheriting from ClapTrap, with:
- Updated default stats (HP/EP/AD according to the subject)
- Overridden
attack()(ScavTrap-style message) - New ability:
guardGate()
Concepts practiced:
publicinheritance and overriding- Derived class constructors calling base constructors
Second derived class: high energy, friendly vibes.
Goal:
Create FragTrap inheriting from ClapTrap, with:
- Updated default stats (HP/EP/AD)
- New ability:
highFivesGuys()
Concepts practiced:
- More inheritance practice
- Consistent canonical form across classes
Multiple inheritance + the “diamond” problem.
Goal:
Create DiamondTrap inheriting from both FragTrap and ScavTrap, while:
-
Keeping one shared
ClapTrapbase (via virtual inheritance) -
Having its own
name(DiamondTrap identity) -
Storing the base name as
DiamondTrapName_clap_name -
Using
ScavTrap::attack() -
Adding
whoAmI()to print both names:- DiamondTrap’s own name
- ClapTrap’s name
Concepts practiced:
- Multiple inheritance
- Ambiguity resolution (
Base::method()) virtual public Baseto ensure a single shared base
From the subject (typical for 42 C++ modules):
-
Compiler:
c++ -
Flags:
-Wall -Wextra -Werror-std=c++98
-
OS: any Unix-like system (Linux / macOS)
-
No external libraries (no C++11+)
Clone the repository and build each exercise separately.
git clone <this-repo-url>
cd cpp-module-03cd ex00
make
./claptrapcd ex01
make
./scavtrapcd ex02
make
./fragtrapcd ex03
make
./diamondtrapExecutable names may differ depending on your Makefiles / implementation.
cpp-module-03/
├── ex00/
│ ├── Makefile
│ ├── ClapTrap.hpp
│ ├── ClapTrap.cpp
│ └── main.cpp
│
├── ex01/
│ ├── Makefile
│ ├── ClapTrap.hpp / ClapTrap.cpp
│ ├── ScavTrap.hpp / ScavTrap.cpp
│ └── main.cpp
│
├── ex02/
│ ├── Makefile
│ ├── ClapTrap.hpp / ClapTrap.cpp
│ ├── FragTrap.hpp / FragTrap.cpp
│ └── main.cpp
│
└── ex03/
├── Makefile
├── ClapTrap.hpp / ClapTrap.cpp
├── ScavTrap.hpp / ScavTrap.cpp
├── FragTrap.hpp / FragTrap.cpp
├── DiamondTrap.hpp / DiamondTrap.cpp
└── main.cpp
- Check that HP/EP/AD values match the subject defaults in each class.
- Verify that energy can run out and actions stop when EP = 0.
- Verify that dead traps (HP = 0) cannot attack/repair.
-
Confirm
ClapTrapconstructor is called once (shared base). -
Confirm
DiamondTrap::attack()uses ScavTrap-style output. -
Confirm
whoAmI()prints:DiamondTrap name: RubyClapTrap name: Ruby_clap_name
-
Confirm destructor order is reasonable (derived → bases, virtual base last).
Mental model:
Human= base classWarrior= derived class
class Warrior : public Human {};void greet(Human& h);
Warrior w;
greet(w); // ✅ OK (is-a)
Human* p = &w; // ✅ OK
Human& r = w; // ✅ OK- Classic OOP is-a relationship
- Typical for school exercises (
ScavTrapis-aClapTrap)
class Warrior : protected Human {};void greet(Human& h);
Warrior w;
greet(w); // ❌ ERROR (outside can’t treat Warrior as Human)
Human* p = &w; // ❌ ERROR
Human& r = w; // ❌ ERRORvoid train(Warrior& w);
Warrior w;
train(w); // ✅ OKclass Warrior : protected Human
{
public:
void demo()
{
attack(); // ✅ if attack() was public/protected in Human
hitPoints = 10; // ✅ if hitPoints was protected in Human
}
};class EliteWarrior : public Warrior
{
public:
void demo()
{
attack(); // ✅ base is visible as protected
hitPoints = 99; // ✅ if it was protected in Human
}
};- Rare
- You want to hide “is-a” from outside, but but keep the base accessible for subclasses.
class Warrior : private Human {};
// For `class`, this is the default:
class Warrior : Human {}; // ❗ private inheritance by defaultvoid greet(Human& h);
Warrior w;
greet(w); // ❌ ERROR
Human* p = &w; // ❌ ERROR
Human& r = w; // ❌ ERRORclass EliteWarrior : public Warrior
{
public:
void demo()
{
attack(); // ❌ ERROR (Human part became private inside Warrior)
hitPoints = 99; // ❌ ERROR
}
};class Warrior : private Human
{
public:
void demo()
{
attack(); // ✅ OK inside Warrior
hitPoints = 10; // ✅ if protected in Human
}
};class Warrior : private Human
{
public:
using Human::attack; // ✅ make only this base method public
void specialMove();
};Or forward manually:
class Warrior : private Human
{
public:
void attackPublic(const std::string& target)
{
attack(target); // ✅ call base inside
}
};- Rare (often composition is better)
- When inheritance is only for code reuse, not for “is-a”
class A : B {}; // = private inheritance (default)
struct A : B {}; // = public inheritance (default)public→ IS-Aprotected→ IS-A only for subclassesprivate→ implementation detail (code reuse), not IS-A
A compact list of keywords/idioms that matter most around inheritance.
If a base method is virtual, a call through Base& / Base* will dispatch to the derived override.
class Base {
public:
virtual void speak();
};
class Der : public Base {
public:
void speak() override;
};Why: “one interface — multiple implementations”.
void speak() override;If the signature doesn’t match exactly (params, const, refs, etc.), you get a compile error.
class Boss final {};class Base {
public:
virtual void speak() final;
};If you have polymorphism (virtual methods) and you might do delete basePtr;, the base destructor should almost always be virtual.
class Base {
public:
virtual ~Base();
};
class Der : public Base {
public:
~Der();
};
Base* p = new Der();
delete p; // ✅ calls ~Der(), then ~Base()Without virtual ~Base() you risk only ~Base() being called.
5) protected — visible to derived classes, hidden from outside 🛡️
class Base {
protected:
int hp;
};Idea: derived classes can access it; external code cannot.
A derived method with the same name can hide base overloads (name hiding).
class Base {
public:
void attack(int);
void attack(double);
};
class Der : public Base {
public:
using Base::attack; // ✅ bring base overloads back into scope
void attack(const char*);
};Also handy with private inheritance: you can expose only specific base methods.
class Money {
public:
explicit Money(int cents);
};
Money m = 42; // ❌ not allowed
Money m2(42); // ✅ allowedclass Base {
public:
Base() = default;
Base(const Base&) = delete;
Base& operator=(const Base&) = delete;
virtual ~Base() = default;
};Why: some bases must not be copyable (resources, ownership, etc.).
Useful with overriding / multiple inheritance.
void Der::attack(const std::string& target)
{
Base::attack(target); // call base implementation
}virtualon methods → polymorphism (override/final)virtualin inheritance (class A : virtual public B) → a single shared base in the “diamond”
-
C++ modules at 42 generally target C++98 (no modern features unless explicitly allowed).
-
There is no Norminette for C++, but clean structure and readable output logs help a lot during evaluation.
-
If your logs differ slightly, check:
- Constructor/destructor order
- Which class version of
attack()is being called - Whether your base is truly shared in the diamond (
virtual public ClapTrap)
If you’re a 42 student working on the same module: feel free to explore, get inspired, but write your own implementation — that’s where the learning happens. 🚀