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

CPP Module 01

Memory allocation, pointers to members, references and switch statements


On Themyscira we trained with what we had.
We learned to use every weapon, to know its weight, when to hold it close and when to let it go.

A warrior who does not know the reach of her weapon is dangerous to herself.

C++ gives you 2 ways to hold memory: on the stack and on the heap.
CPP01 teaches you the difference.

CPP Module 01:

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

Exercise 00: BraiiiiiiinnnzzzZ · ex00/ · Stack vs heap zombie
Exercise 01: Moar brainz! · ex01/ · Heap array of objects
Exercise 02: HI THIS IS BRAIN · ex02/ · References vs pointers
Exercise 03: Unnecessary violence · ex03/ · References as members
Exercise 04: Sed is for losers · ex04/ · File I/O with streams
Exercise 05: Harl 2.0 · ex05/ · Pointers to member functions
Exercise 06: Harl filter · ex06/ · Switch statement

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

Previously…


Stack vs Heap

A warrior’s spear thrown in training disappears when the session ends.
A sword forged and stored in the armory stays until someone puts it away.

Stack Heap
How Zombie z("Ares"); Zombie* z = new Zombie("Ares");
Lifetime Ends at closing } Until you call delete
Cleanup Automatic Manual (your responsibility)
Destructor called Yes, automatically Only when you delete
void    stackExample(void)
{
    Zombie  z("Steve"); // created here
}                       // destroyed here (destroyer called)

Zombie* heapExample(void)
{
    return new Zombie("Ares");  // survives the function
}           // must delete it or leaks

[!WARNING] Every new must be paired with a delete. Every new[] with a delete[].


References

A reference is an alias.
Not a copy, nor a pointer, rather the same thing, under a different name.

std::string  weapon = "sword";
std::string  &alias = weapon;   // alias IS weapon

alias = "lasso";                // weapon is now "lasso"

Compared to pointers:

Pointer Reference
Syntax int* p = &x; int& r = x;
Can be null Yes No
Can be reassigned Yes No (bound at declaration, forever)
Dereferencing *p just r

[!TIP] Use const & when passing objects into functions you won’t modify as it avoids a copy without risking mutation.

void    logName(std::string const &name);   // reads name, no copy made

Pointers to Member functions

A warrior is not just a body: she carries a set of moves.
You can point to the move itself and call it later.

class Harl {
public:
    void    complain(std::string level);
private:
    void    debug(void);
    void    info(void);
};

// A pointer to a member function of Harl that takes void and returns void
void    (Harl::*ptr)(void) = &Harl::debug;

Harl    h;
(h.*ptr)(); // calls h.debug()

The syntax is unusual but it reads: “call ptr on object h.”


File I/O

std::ifstream reads files. std::ofstream writes them.
Both work like std::cin / std::cout.

#include <fstream>

std::ifstream   in("input.txt");
std::ofstream   out("output.txt");

if (!in.is_open())
    // handle error

std::string line;
while (std::getline(in, line))
    out << line << "\n";

[!NOTE] Both streams close automatically when they go out of scope, so n manual close is required.


Switch

Exercise 06 introduces switch.

switch (level)
{
    case 0:
        debug();
        break;  // without break, execution falls through to the next case
    case 1:
        info();
        break;
    default:
        complain();
}

Fallthrough is intentional in some designs.
Unintentional fallthrough is a bug that compilers won’t always catch.


Traps

Forgetting delete[] for arrays

Zombie* horde = new Zombie[n];
delete[] horde; // not delete horde

Returning a reference to a local variable

std::string& bad(void)
{
    std::string local = "gone";
    return local;   // undefined behavior since local is destroyed after return
}

Initializing a reference member outside the initialization list
Reference members must be initialized in the constructor initialization list.
You cannot assign them in the body

// WRONG !!!    
HumanA::HumanA(std::string name, Weapon &weapon)
{
    _weapon = weapon;   // compile error if _weapon is a reference
}

// Right
HumanA::HumanA(std::string name, Weapon &weapon) : _weapon(weapon)
{
}

Instructions

# ──── ex00 ────
cd ex00 && make
./zombie
valgrind --leak-check=full ./zombie

# ──── ex01 ────
# All N zombies announce, all destroyed together
cd ex01 && make
./horde
valgrind --leak-check=full ./horde

# ──── ex02 ────
# Pointer and reference both point to the same string (Addresses match)
cd ex02 && make
./brain

# ──── ex03 ────
# HumanA always has a weapon. HumanB can exist without one.
cd ex03 && make
./violence

# ──── ex04 ────
cd ex04 && make
echo "The lasso reveals all truths." > test.txt
./sed_is_for_losers test.txt lasso "Lasso of Truth"
cat test.txt.replace

# ──── ex05 ────
cd ex05 && make
./harl DEBUG
./harl INFO
./harl WARNING
./harl ERROR
./harl NONSENSE   # should produce no output or a default

# ──── ex06 ────
cd ex06 && make
./harlFilter DEBUG
./harlFilter WARNING
./harlFilter ERROR
./harlFilter NONSENSE

Resources


You can convince yourself the leaks are small.
You can tell yourself you will fix them later.
Run Valgrind, and it will tell you the truth whether you want it or not.

The Lasso does not lie and neither does Valgrind.