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

CPP Module 00

Namespaces, classes, member functions, stdio streams, initialization lists, static, const, and some other basic stuff


You’ve spent months writing C. Malloc and free, pointers to pointers, structs passed by address, header files full of typedefs.
You understood the machine and you felt in control.

And now someone has handed you a new language and said: start over.

I understand the feeling… When I left Smallville, I had to build a new understanding of the world from scratch.

C is your Smallville. C++ is Metropolis: a larger world, with more structure, more people to protect, more ways things can go wrong if you’re not disciplined.

Let me show you around.


Description

CPP Module 00:

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

Exercise 00: Megaphone · ex00/ · Makefile, megaphone.cpp
Exercise 01: My Awesome PhoneBook · ex01/ · Makefile, .cpp, .hpp
Exercise 02: The Job of Your Dreams · ex02/ · Makefile, Account.cpp, Account.hpp, tests.cpp · not mandatory

Forbidden without exception: printf · *alloc · free · using namespace · friend · STL containers · STL algorithms

What CPP00 is teaching you

My father Jor-El gave me knowledge. But knowledge without structure is just noise.
Before he could teach me anything useful, he had to give me a framework to hold it all.

That is what CPP00 does for C++.

You already know how to write a program. You know memory, you know pointers.
CPP00 won’t teach you to program again. But it will teach you a new way to organize what you build.

Concept Meaning
Namespaces Separate territories that prevent naming conflicts
Classes Structures that carry both data and the functions that operate on that data
Member functions Functions that belong to a class and live inside it
Streams C++’s Input/Output system. std::cout instead of printf
Static members Data or functions that belong to the class itself, not to any individual object
const methods Functions that promise not to alter the object they belong to

The 3 exercises introduce these progressively: - Megaphone is five minutes of warm-up, essentially teaching you that C++ has its own way of doing things. And that way starts with streams and strings. - PhoneBook is the real lesson. Write your first 2 classes, understand public and private, manage state, handle I/O. - Account is a reading exercise as much as a writing one. You’re handed an interface and a log, and you deduce the implementation.

The rules you cannot break

[!WARNING] Violating any of these rules ends the evaluation immediately.

Violation Grade
printf, *alloc, free 0
using namespace <name> -42
friend keyword -42
STL containers or algorithms in the code -42
Function body implemented inside a header 0
No include guards 0

These rules exist because code lives longer than the person who writes it. The discipline you build now protects the people who read your code later.


Warming up

Megaphone, the first broadcast

In Metropolis, when I need to be heard across the city, I don’t whisper. I project clearly and without distortion.

Megaphone is that exercise:
Given words as command line arguments, you shout them in uppercase.
Given nothing, you broadcast the standard emergency signal.

The required behavior:

$ ./megaphone "shhhhh... I think the students are asleep..."
SHHHHH... I THINK THE STUDENTS ARE ASLEEP...

$ ./megaphone
* LOUD AND UNBEARABLE FEEDBACK NOISE *

The C++ way:

#include <iostream>
#include <cctype>

int main(int ac, char **av)
{
    if (ac == 1)
    {
        std::cout << "* LOUD AND UNBEARABLE FEEDBACK NOISE *\n";
        return (0);
    }
    for (int i = 1; i < ac; i++)
    {
        for (int j = 0; av[i][j] != '\0'; j++)
            std::cout << (char)std::toupper((unsigned char)av[i][j]);
    }
    std::cout << std::endl;
    return (0);
}

[!NOTE] The cast to (unsigned char) before std::toupper is not decoration.
On platforms where char is signed, characters with values above 127 would produce undefined behavior without it.
Do the right thing even when the compiler doesn’t force you to!

[!CAUTION] The return value of std::toupper is int.
The (char) cast after the call converts it back so that it prints the character.
Without it, we would get the ASCII number instead.


Every C++ Concept, from C to Clark Kent

This section explains every new concept CPP00 introduces, grounded in what you already know from C.

Namespaces: separate territories

In C, name collisions were your problem to solve. You prefixed everything: ft_strlen, ft_split, … It worked, but it was a convention, not a language feature.

In C++, namespaces are built in:

namespace std
{
    // the entire standard library lives here (cout, cin, string, ...)
}

To use anything from the standard library, you prefix it with std::

std::cout << "hello" << std::endl;
std::string name = "Kal-El";

[!WARNING] The rule using namespace std; is forbidden in this module.
It also matters outside of 42:
In any large project, pulling an entire namespace into scope defeats the purpose of having namespaces at all.
You risk silent name conflicts, and you give up clarity about where each name comes from.

The Cherno | Why I don’t “using namespace std”


Classes (the dual identity)

Here is the central metaphor of this entire module and it maps perfectly.

Clark Kent is the internal state. He has history and knows things no one else knows.
He is not hidden out of shame. Rather, he is protected because not everything should be exposed to everyone.

Superman is the public interface. When you call makeDeposit(), you are interacting with Superman.

The account adjustments, the logging, the internal counter increments… Those happen inside Clark Kent, invisible to you.

In C, you would write:

typedef struct s_account
{
    int index;
    int amount;
    int nb_deposits;
} t_account;

void account_deposit(t_account *a, int amount)
{
    a->amount += amount;
}

Nothing stops anyone from reaching into t_account and setting amount to anything they want, therefore bypassing all logic.

In C++:

class Account {
public:
    void    makeDeposit(int deposit);     // Superman
    int     checkAmount(void) const;

private:
    int     _amount;                      // Clark Kent
    int     _nbDeposits;
};

Now _amount is inaccessible from outside the class.
The only way to change it is through makeDeposit, which applies all the correct logic.

[!NOTE] Anything that external code does not need to see is private.
Anything that external code needs to call is public.
When in doubt, make it private.


Access specifiers (what the world sees)

Specifier Who can access
public Any code, anywhere
private Only methods of this class
protected This class and its subclasses (seen in later modules)

The difference between struct and class in C++ is exactly 1 thing:
struct members are public by default, class members are private by default.

In practice at 42, we use class for OOP objects.


Member functions: functions that live inside the class

In C, functions and data are separate. You pass a pointer to your struct and operate on it:

void contact_set_name(t_contact *c, const char *name)
{
    ft_strlcpy(c->first_name, name, 50);
}

In C++, the function lives inside the class and has direct access to the private members:

void Contact::setFirstName(std::string const &val)
{
    _firstName = val;   // directly accesses the private field
}

The :: operator is the scope resolution operator.

Contact::setFirstName means “the setFirstName that belongs to Contact.”

[!NOTE] When you call a method, C++ automatically passes a hidden pointer to the current object. It’s called this.
So _firstName = val is internally this->_firstName = val.


Constructors and destructors (arrival and departure)

When I arrive at a scene, things change. Resources are engaged and a presence is established.
When I leave, I make sure the situation is clean: no loose ends, no collateral left unaddressed.

That is basically what constructors and destructors do.

Constructor:

called automatically when an object is created. Same name as the class, no return type.

Account::Account(int initial_deposit)
    : _accountIndex(_nbAccounts),    // initialization list
    _amount(initial_deposit),
    _nbDeposits(0),
    _nbWithdrawals(0)
{
    _nbAccounts++;
    _totalAmount += initial_deposit;
    _displayTimestamp();
    std::cout << "index:" << _accountIndex << ";amount:" << _amount << ";created" << std::endl;
}

The : member(value) syntax after the constructor signature is the initialization list.
It initializes members before the constructor body runs and it’s more efficient than assigning inside the body.

Destructor: called automatically when an object goes out of scope or is deleted. Same name as the class prefixed with ~.

Account::~Account(void)
{
    _displayTimestamp();
    std::cout << "index:" << _accountIndex << ";amount:" << _amount << ";closed" << std::endl;
}

When a local variable reaches the end of its scope (the closing }), its destructor is called automatically.
No manual cleanup function!


Static members (the Fortress of Solitude)

The Fortress of Solitude does not belong to any particular moment in my life. It is not created fresh each time I arrive and destroyed when I leave.
It exists independently and is shared across every visit.

And static class members work the same way.

Static data member: 1 variable shared by all instances of the class.
Every Account object shares the same _nbAccounts. When the constructor increments it, every future Account sees the updated value.

// In Account.hpp (declaration)
private:
    static int _nbAccounts;

// In Account.cpp (definition)
int Account::_nbAccounts = 0;

[!WARNING] If you forget the definition in the .cpp file, you get a linker error:

undefined reference to `Account::_nbAccounts`

The declaration in the header only says “this variable exists.” The definition in the .cpp says “allocate the memory here.”
That’s why both lines are required.

Static method: a function that belongs to the class, not to any specific object. It has no this pointer and it can only access static members.

int Account::getNbAccounts(void)
{
    return (_nbAccounts);   // OK: static member
    // return (_amount);    // ERROR: _amount is per-instance, no 'this' here
}

You call static methods on the class, not on an object: Account::getNbAccounts().


const methods: observing without interfering

There are moments when I am not there to act. Only to observe and understand.
I do not rearrange the scene. I do not change anything.

A const method makes that promise formally:

int Account::checkAmount(void) const
{
    return (_amount);   // read-only, fine
    // _amount = 5;     // compile error: can't modify in a const method
}

The const after the closing parenthesis tells the compiler: “this method will not modify any member variables.”
If you try to, you get a compile error.

Rule:

if a method doesn’t need to modify the object, make it const.


std::string (the translator)

On Krypton, information was stored in crystalline matrices with their own access rules. Humans communicate much easier.

std::string is a translation layer. It takes the work of managing character data (allocation, reallocation, and all the shenanigans) and handles it for you.

Task C C++
Declaration char name[50]; std::string name;
Assignment ft_strlcpy(name, "Clark", 50) name = "Clark";
Comparison ft_strcmp(a, b) == 0 a == b
Length ft_strlen(name) name.length()
Concatenation ft_strcat(dest, src) dest += src;
Substring manual loop name.substr(0, 9)
Empty check name[0] == '\0' name.empty()

std::string manages its own memory so you never call malloc or free
When a std::string goes out of scope, its destructor releases the memory automatically.

[!NOTE] The subject says “no dynamic allocation” in Exercise 01.
This refers to the Contact _contacts[8] array as it must live on the stack.
Using std::string inside Contact is perfectly fine.


Streams (the printing press)

The Daily Planet runs on its printing press. Information goes in, gets formatted and shaped, and then it comes out in a form that people can easily read.

std::cout and std::cin are C++’s I/O system.
They replace printf and scanf entirely.

Output:

// C
printf("index: %d, amount: %d\n", index, amount);

// C++
std::cout << "index:" << index << ";amount:" << amount << std::endl;

<< is the insertion operator.
It chains output operations left to right, like a pipeline
std::endl flushes the buffer and adds \n.

Input:

// Read one word (stop at whitespace)
std::string word;
std::cin >> word;

// Read a full line (including spaces)
std::string line;
std::getline(std::cin, line);

[!IMPORTANT] std::cin >> word leaves the newline character in the buffer.
If you mix >> with getline, the next getline may read an empty string (just that leftover newline).
So use getline consistently, as we will do in the PhoneBook.

Checking for EOF:

if (!std::getline(std::cin, line))
    break;  // Ctrl+D pressed, stream ended

When the stream ends (EOF), getline returns false.


References (a cleaner way to pass data)

In C, to pass data into a function without copying it, you passed a pointer:

void set_name(const char *name)
{
    ...
}

C++ introduces references, aliases for existing variables:

void Contact::setFirstName(std::string const &val)
{
    ...
}

std::string const &val means: “val is a reference to a const string, so I can read it but not modify it.”

Benefits over pointers:

When to use const &: any time you pass an object into a function and don’t need to modify it.
It avoids an unnecessary copy of potentially large data.


Include guards (the identity check)

The Fortress of Solitude has protocols.
Not everyone who arrives gets in and the same visitor doesn’t get processed twice.

Include guards prevent a header from being included more than once in the same compilation.

#ifndef CONTACT_HPP
# define CONTACT_HPP

// content here

#endif

Without guards: if 2 .cpp files include the same header, the compiler sees the class declared twice and fails with a redefinition error.

W/ guards: the first inclusion defines CONTACT_HPP. The second inclusion finds it already defined and skips the entire file.

[!NOTE] #pragma once does the same but in 1 line:

#pragma once
// content here

The preprocessor marks the file itself. If it’s included again, the whole file is skipped.
It’s not part of the C++ standard but supported by every major compiler (GCC, Clang included)
It’s also provides slightly faster compilation in some implementations since the compiler can skip the file before even opening it.


iomanip (precision formatting)

Metropolis has standards: the buildings are aligned, the streets are measured, the signal is broadcast at just the right frequency.

The <iomanip> header provides the tools to format output with that kind of precision:

Manipulator Effect Example Output
std::setw(n) Sets minimum field width to n for the next output only std::setw(6) << "hi" ‎ ‎ ‎ ‎ hi
std::setfill(c) Sets the fill character (default: space) std::setfill('-') << std::setw(6) << "hi" ----hi
std::right Right aligns text within the field width std::setw(6) << std::right << "hi" ‎ ‎ ‎ ‎ hi
std::left Left aligns text within the field width std::setw(6) << std::left << "hi" hi‎ ‎ ‎ ‎

[!NOTE] std::setw only affects the next output operation, then resets automatically.
std::setfill persists.
If we set setfill('0') for a timestamp and don’t reset it, the next setw in SEARCH might pad with 0 instead of spaces.


Instructions

Exercise 00

cd ex00
make

./megaphone

./megaphone "shhhhh... I think the students are asleep..."

./megaphone a b c

Exercise 01

cd ex01
make

# Empty search
echo -e "SEARCH\nEXIT" | ./phonebook

# Basic add and search
printf "ADD\nLois\nLane\nSuperwoman\n555-1938\nClark’s glasses aren’t fooling anyone\nSEARCH\n0\nEXIT\n" | ./phonebook

# Truncation test (first name > 10 chars)
printf "ADD\nDoomsdayDestroyerOfWorlds\nBloome\nDavis\n555-1992\nKilled Superman once\nSEARCH\n0\nEXIT\n" | ./phonebook

# Invalid indexes
printf "ADD\nLois\nLane\nSuperwoman\n555-1938\nClark’s glasses aren’t fooling anyone\nSEARCH\n99\nEXIT\n" | ./phonebook
printf "ADD\nJimmy\nOlsen\nMy pal\n555-1954\nOwns a signal watch for emergencies\nSEARCH\nabc\nEXIT\n" | ./phonebook

# Empty field rejection (reprompt for first name)
printf "ADD\n\nLois\nLane\nSuperwoman\n555-1938\nClark’s glasses aren’t fooling anyone\nEXIT\n" | ./phonebook

# Circular replacement: add 9, check that index 0 holds the 9th
# (9th contact replaces the first)
printf "ADD\nLana\nLang\nInsect Queen\n555-1946\nShe wore the suit so Lex could not\nADD\nLois\nLane\nSuperwoman\n555-1938\nClark's glasses aren't fooling anyone\nADD\nJimmy\nOlsen\nMy pal\n555-1954\nOwns a signal watch for emergencies\nADD\nLex\nLuthor\nGenius\n555-1941\nObsessed with kryptonite\nADD\nPerry\nWhite\nEditor\n555-1942\nGreat Caesar ghost\nADD\nVril\nDox\nBrainiac\n555-1943\nShrinks cities for fun\nADD\nGeneral\nZod\nKneel\n555-1944\nBanished from Krypton\nADD\nJonathan\nKent\nPa Kent\n555-1945\nTaught Clark to hide powers\nSEARCH\n0\nADD\nKara\nZor-El\nSupergirl\n555-1984\nActually stronger than her cousin\nSEARCH\n0\nEXIT\n" | ./phonebook

# EOF clean exit
echo -n "" | ./phonebook

make re

Exercise 02

cd ex02
make

# Compare output structure with 19920104_091532.log (ignoring timestamps)
./tests

# Strip timestamps from both and diff them
# The last 8 lines (destructors) may differ but everything else must match
./tests | sed 's/\[.*\]/[TS]/g' > /tmp/mine.txt
sed 's/\[.*\]/[TS]/g' ./19920104_091532.log > /tmp/ref.txt
diff /tmp/mine.txt /tmp/ref.txt

make re

Resources


The transition from C to C++ is not asking you to abandon what you learned. It is requiring that you organize it better.
While C taught you how the machine works, C++ teaches you how to build systems that other people can understand and trust.

You already know how to write code that works. Now go write code that holds.

Somewhere, in our darkest night, we made up the story of a language that would never let us down…