https://github.com/tigran-sargsyan-w/cpp-module-04
This module is designed to help you understand subtype polymorphism, abstract classes, and interfaces in C++.
https://github.com/tigran-sargsyan-w/cpp-module-04
42 42-school abstract-class cpp cpp-modules cpp98 deep-copy inheritance interface memory-management object-oriented-programming oop polymorphism pure-virtual rule-of-three subtype-polymorphism virtual-functions
Last synced: 8 days ago
JSON representation
This module is designed to help you understand subtype polymorphism, abstract classes, and interfaces in C++.
- Host: GitHub
- URL: https://github.com/tigran-sargsyan-w/cpp-module-04
- Owner: tigran-sargsyan-w
- Created: 2025-12-18T21:12:05.000Z (6 months ago)
- Default Branch: main
- Last Pushed: 2026-01-11T10:12:08.000Z (5 months ago)
- Last Synced: 2026-05-06T11:49:04.027Z (about 1 month ago)
- Topics: 42, 42-school, abstract-class, cpp, cpp-modules, cpp98, deep-copy, inheritance, interface, memory-management, object-oriented-programming, oop, polymorphism, pure-virtual, rule-of-three, subtype-polymorphism, virtual-functions
- Language: C++
- Homepage:
- Size: 48.8 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# C++ Module 04 β Subtype Polymorphism, Abstract Classes & Interfaces πΎπ§ π§
β
**Status**: Completed β all exercises
π« **School**: 42 β C++ Modules (Module 04)
π
**Score**: 100/100
> *Subtype polymorphism, virtual functions, deep copies, abstract classes, and βinterfacesβ via pure abstract classes (C++98).*
---
## π Table of Contents
- [Description](#-description)
- [Goals of the Module](#-goals-of-the-module)
- [Concept Notes](#-concept-notes)
- [Why the base destructor must be virtual](#-why-the-base-destructor-must-be-virtual)
- [Deep copy vs shallow copy](#-deep-copy-vs-shallow-copy)
- [Abstract class vs interface in C++98](#-abstract-class-vs-interface-in-c98)
- [Why write `virtual` in derived classes? What about `override`?](#-why-write-virtual-in-derived-classes-what-about-override)
- [Exercises Overview](#-exercises-overview)
- [ex00 β Polymorphism](#ex00--polymorphism)
- [ex01 β I donβt want to set the world on fire](#ex01--i-dont-want-to-set-the-world-on-fire)
- [ex02 β Abstract class](#ex02--abstract-class)
- [ex03 β Interface & recap](#ex03--interface--recap)
- [Requirements](#-requirements)
- [Build & Run](#-build--run)
- [Repository Layout](#-repository-layout)
- [Testing Tips](#-testing-tips)
- [42 Notes](#-42-notes)
---
## π Description
This repository contains my solutions to **42βs C++ Module 04 (C++98)**.
The module focuses on **runtime polymorphism** (virtual functions), **proper destruction through base pointers**, **deep-copy ownership** (Rule of Three), plus **abstract classes and interface-like design** using pure virtual methods.
---
## π― Goals of the Module
Concepts covered (depending on the exercise):
- Subtype polymorphism: **virtual functions**, virtual dispatch, overriding
- Correct destruction: **virtual destructors** when deleting through base pointers
- Composition + ownership: `Brain*` inside `Dog` / `Cat` + proper `new/delete`
- **Deep copy** vs shallow copy (copy constructor + assignment operator)
- Abstract classes (non-instantiable base)
- βInterfacesβ via **pure abstract classes** (`= 0`)
---
## π§© Concept Notes
### β
Why the base destructor must be virtual
If you ever delete an object through a **base pointer**, the base class **must** have a `virtual` destructor.
```cpp
Animal* a = new Dog();
delete a; // must call ~Dog() then ~Animal()
```
* If `~Animal()` is **not virtual**, the program may call only `~Animal()`.
* Result: derived cleanup is skipped β leaks (e.g. `Brain*`) and/or undefined behavior.
β
Practical rule:
* **If a class is meant to be used polymorphically (has virtual methods), give it a virtual destructor.**
---
### 𧬠Deep copy vs shallow copy
When a class owns heap memory (like `Brain*`), copying must be a **deep copy**.
**Shallow copy (bad):** copies only the pointer.
```cpp
this->brain = other.brain; // β both objects share the same Brain
```
Problems:
* shared state (changing ideas in one changes the other)
* double `delete` (crash)
**Deep copy (good):** allocates a new resource and copies the content.
```cpp
this->brain = new Brain(*other.brain); // β
independent copy
```
This is why ex01/ex02 naturally push you to the **Rule of Three**:
* destructor
* copy constructor
* copy assignment operator
---
### π§± Abstract class vs interface in C++98
In C++98 we donβt have a special `interface` keyword β we build it using **pure abstract classes**.
**Abstract class (common at 42):**
* has at least one pure virtual method (`= 0`)
* cannot be instantiated
* may contain data and some shared implementation
```cpp
class Animal {
public:
virtual ~Animal() {}
virtual void makeSound() const = 0; // pure
};
```
**Interface-style class (ex03 style):**
* usually *only* pure virtual methods
* no data
* always a virtual destructor
```cpp
class ICharacter {
public:
virtual ~ICharacter() {}
virtual std::string const& getName() const = 0;
virtual void equip(AMateria* m) = 0;
virtual void unequip(int idx) = 0;
virtual void use(int idx, ICharacter& target) = 0;
};
```
---
### π§· Why write `virtual` in derived classes? What about `override`?
**Key fact:** once a method is `virtual` in the base class, it stays virtual in all derived classes β even if you donβt repeat the word `virtual`.
```cpp
class Animal { public: virtual void makeSound() const; };
class Dog : public Animal {
public:
void makeSound() const; // β
still virtual (because Animalβs is virtual)
// virtual void makeSound() const; // β
optional, for readability
};
```
So why do people still write `virtual` again?
* **Readability**: makes it obvious this method is part of polymorphism.
* **Consistency** in headers.
But repeating `virtual` does *not* protect you from the most common bug:
* **signature mismatch** (accidentally not overriding)
Example:
```cpp
class Animal { public: virtual void makeSound() const; };
class Dog : public Animal {
public:
void makeSound(); // β not overriding (missing const) β creates a different function
};
```
In modern C++ (C++11+), youβd write:
```cpp
void makeSound() const override;
```
That forces the compiler to error if it doesnβt actually override.
In **C++98**, `override` does not exist, so common alternatives are:
* repeat the exact signature carefully (including `const`)
* optionally write a comment:
```cpp
virtual void makeSound() const; // override
```
---
## π¦ Exercises Overview
### ex00 β Polymorphism
**Goal:**
Implement a base class `Animal` with a `type` and a `makeSound()` method.
Create derived classes `Dog` and `Cat` that override `makeSound()` so calls through `Animal*` produce the *derived* sound.
Also implement `WrongAnimal` / `WrongCat` to demonstrate what happens when polymorphism is done βwrongβ (e.g., missing `virtual`).
**Concepts practiced:**
* `virtual` functions and overriding
* Polymorphic behavior via base pointers/references
* Why forgetting `virtual` breaks dynamic dispatch
---
### ex01 β I donβt want to set the world on fire
**Goal:**
Add a `Brain` class with `std::string ideas[100]`.
`Dog` and `Cat` must own a `Brain*` and manage it with `new` / `delete`.
In `main`, create an array of `Animal*` (half dogs / half cats), then `delete` them via `Animal*` and ensure destructors are called correctly and there are **no leaks**.
Copies of `Dog` and `Cat` must be **deep copies** (no shared Brain).
**Concepts practiced:**
* Rule of Three (copy ctor, assignment, destructor)
* Deep copy ownership & avoiding double free / leaks
* Polymorphic deletion order
---
### ex02 β Abstract class
**Goal:**
Make the base `Animal` **non-instantiable** (because an βAnimalβ doesnβt make sense by itself). Everything should still work as before with `Dog` / `Cat`.
> Optional: rename it to `AAnimal` (depending on your preference / implementation).
**Concepts practiced:**
* Pure virtual functions
* Abstract base classes and intent in API design
---
### ex03 β Interface & recap
**Goal:**
Implement a small Materia system:
* `AMateria` (abstract base): has `type`, `clone()` (pure virtual), and `use()`
* Concrete materias: `Ice` and `Cure`
* `type`: `"ice"` / `"cure"`
* `clone()` returns a new instance of the same type
* `ICharacter` interface + concrete `Character` with an inventory of **4 slots**
* `equip()` puts materia into the first empty slot
* `unequip()` must **not delete** materia
* copies of `Character` must be **deep** (inventory cloned, old deleted)
* `IMateriaSource` interface + `MateriaSource`
* can learn up to **4** materia βtemplatesβ (store copies)
* can `createMateria(type)` by cloning learned template, or return `0` if unknown
**Important note:**
Unequipped materias must still be managed somehow (save addresses, βfloorβ storage, etc.) to avoid leaks.
**Concepts practiced:**
* Interface design in C++98 (pure abstract classes)
* Cloning pattern / prototype pattern
* Ownership rules and memory safety
---
## π Requirements
From the subject:
* **Compiler**: `c++`
* **Flags**: `-Wall -Wextra -Werror` (and it must compile with `-std=c++98`)
* **No external libraries**
* Forbidden: `printf`, `malloc`, `free` (and family)
* Until later modules: **no STL containers / algorithms** (no ``, no ``, etc.)
* βGoodbye Norminette!β (no enforced C norm), but code must stay readable
---
## βΆοΈ Build & Run
```bash
git clone
cd cpp-module-04
```
### ex00
```bash
cd ex00
make
./animal
```
### ex01
```bash
cd ex01
make
./animal
```
### ex02
```bash
cd ex02
make
./animal
```
### ex03
```bash
cd ex03
make
./interface
```
> Executable names may vary depending on your Makefiles.
---
## π Repository Layout
```text
cpp-module-04/
βββ ex00/
β βββ Makefile
β βββ main.cpp
β βββ Animal.hpp / Animal.cpp
β βββ Dog.hpp / Dog.cpp
β βββ Cat.hpp / Cat.cpp
β βββ WrongAnimal.hpp / WrongAnimal.cpp
β βββ WrongCat.hpp / WrongCat.cpp
β
βββ ex01/
β βββ Makefile
β βββ main.cpp
β βββ Animal.* Dog.* Cat.*
β βββ Brain.hpp / Brain.cpp
β
βββ ex02/
β βββ Makefile
β βββ main.cpp
β βββ AAnimal/Animal.* Dog.* Cat.* Brain.*
β βββ (same structure as ex01, but base is abstract)
β
βββ ex03/
βββ Makefile
βββ main.cpp
βββ AMateria.* Ice.* Cure.*
βββ ICharacter.hpp Character.*
βββ IMateriaSource.hpp MateriaSource.*
βββ (optional) Floor/Trash manager files
```
---
## π Testing Tips
* **ex00**
* Call `makeSound()` through `Animal*` and verify the derived sound is used
* Verify `WrongCat` behaves like `WrongAnimal` when polymorphism is broken
* **ex01 / ex02**
* Copy a `Dog`/`Cat`, modify ideas in one Brain β the other must not change (deep copy)
* Run leak checks (example):
```bash
valgrind --leak-check=full ./animal
```
* Ensure destructors are called in correct order when deleting through `Animal*`
* **ex03**
* Test inventory boundaries (equip 5th materia β nothing happens)
* Test `unequip()` does not delete; ensure you still free unequipped materias later
* Copy a `Character` and confirm deep copy (materias cloned; originals independent)
* `MateriaSource` creates only known types; unknown β returns `0`
---
## π§Ύ 42 Notes
* Outputs from constructors/destructors should be **distinct per class** to make tests readable.
* This module is heavily about **ownership rules** β if you `new`, you must `delete` exactly once.
* You can pass the module without ex03, but doing it is excellent practice for interfaces + memory management.
---
If youβre a 42 student working on the same module: get inspired, but **write your own implementation** β thatβs where the learning happens. π