This repository contains solutions for the 42 school C++ modules (Cpp00 - Cpp09).
Each exercise contains a Makefile. To compile an exercise:
cd CppXX/exXX
makemake | make all - Compile the program
make clean - Remove object files
make fclean - Remove object files and executable
make re - Recompile everything
make debug - Clean rebuild with debug flags (address sanitizer)
C++ compiler (c++)
C++98 standard
Make
Each module includes exercise links and a cheat-sheet section of the C++ concepts used in that module.
Introduction to C++ basics: namespaces, classes, member functions, stdio streams, initialization lists, static, const, and some other basic stuff.
- ex00: Megaphone - A simple program to convert text to uppercase
- ex01: PhoneBook - A simple phonebook application
- ex02: The Job Of Your Dreams
Generic concepts
- Class concept
- Object concept
- Abstraction and encapsulation
- Public and private attributes
- What are references
- Basic use of streams and classes from the std namespace
- Use of string objects
- Scope operators for class method definitions from another file
- What are constructors and initialization lists
- Use of the this clause
- Use of static attributes and the concept of not belonging to any object
- Static methods, which are the only ones that can modify static variables
- Arrays of objects
- Basic composition concept
Memory allocation, pointers to members, references, switch statement.
- ex00: BraiiiiiiinnnzzzZ - Zombie class implementation
- ex01: Moar brainz! - Zombie horde
- ex02: HI THIS IS BRAIN - References and pointers
- ex03: Unnecessary violence - Weapon class with references
- ex04: Sed is for losers - File manipulation
- ex05: Harl 2.0 - Pointers to member functions
- ex06: Harl filter - Switch statement
Generic concepts
- Dynamic memory allocation with new
- Destroying dynamic memory with delete
- Arrays of objects with default constructor
- Class destructor concept
- Using stringstream to simulate base changes
- Concept of references and differences with pointers
- References passed as parameters to avoid memory duplication
- Pointers to point to objects from an external object without owning them
- References to point to external objects but associated with the class itself
- Using fstream class and string class method to convert to char *
- getline function
- Function pointers and arrays of function pointers (notations)
- Use of switch (internally uses jump tables or binary trees to keep O(1) complexity)
Ad-hoc polymorphism, operator overloading, and Orthodox Canonical class Form.
- ex00: My First Class in Orthodox Canonical Form
- ex01: Towards a more useful fixed-point number class
- ex02: Now we're talking - Operator overloading
Generic concepts
- What are fixed point numbers and why are they used for determinism across different processor architectures regardless of FPU presence
- How they are created with bit shifting and how operations are performed with fixed point numbers
- Construction of orthodox canonical classes (default constructor, copy constructor, default destructor, and overloaded assignment operator to avoid bitwise copy or shallow copy)
- Compile-time polymorphism or ad-hoc polymorphism
- Overloaded operators for direct arithmetic and logical operations between objects
- Overloading operator for ofstream object to print to screen/stdout redirections
- Overloaded operators for post-increment and pre-increment operations on numbers
- Why you should return objects directly in overloaded arithmetic operations but return references in assignment operations (+=, -=, etc.)
Inheritance in C++.
- ex00: Aaaaand... OPEN! - ClapTrap class
- ex01: Serena, my love! - ScavTrap derived class
- ex02: Repetitive work - FragTrap derived class
- ex03: Now it's weird! - DiamondTrap (multiple inheritance)
Generic concepts
- Inheritance
- Inheritance of public, protected, and private attributes
- No inheritance of overloaded operators, constructors, and destructors
- Memory of the base class and the derived class
- Destruction order of an object built through a base class
- What is the virtual table and why it enables polymorphism
- Why base class destructors should be virtual to avoid memory leaks in polymorphism
- Calling base class constructors from the derived class to build the base part
- Avoid calling base class constructors from derived classes if the base has a default constructor
- Dynamic vs static polymorphism (concept)
- Memory overhead due to the virtual table
- What is object slicing
- Using pointers and references to the base class for virtual method polymorphism in function parameters and class methods
- What is object slicing and why you can't assign a derived object to a base object
- Inheritance as specialization and composition for scalable class systems
- Diamond classes and why you must inherit from the base class virtually to avoid duplicating the base part in the diamond class
- Diamond class constructors must call the base class constructor
Subtype polymorphism, abstract classes and interfaces.
- ex00: Polymorphism - Animal, Dog, Cat classes with virtual functions
- ex01: I don't want to set the world on fire - Adding Brain class for deep copy
- ex02: Abstract class - Animal as an abstract class with pure virtual function
- ex03: Interface & recap - Materia system with interfaces (AMateria, ICharacter, IMateriaSource)
Generic concepts
- Polymorphism and memory scopes in polymorphism
- Attribute hiding (shadowing)
- Array of references notations in function parameters and return types
- Shallow copy vs deep copy for dynamic memory
- Abstract classes (non-instantiable but serve as base for polymorphism)
- Interfaces or pure abstract classes
- Non-constructible: serve as base for pointers and references in polymorphism
- Class composition and circular dependencies
- Const references as return values in getters to save memory and ensure attribute encapsulation
Exceptions.
- ex00: Bureaucrat - Implementation of a Bureaucrat class with grade validation and exception handling
- ex01: Form - Adding a Form class that interacts with the Bureaucrat class, including signing functionality
- ex02: Advanced Forms - Introduction of abstract forms (AForm) and specific form types like PresidentialPardonForm, RobotomyRequestForm, and ShrubberyCreationForm
- ex03: Intern - Implementation of an Intern class that can create forms dynamically based on input
Generic concepts
- How
try,throw, andcatchclauses work. - Types of data that can be thrown with
throw, thestd::exceptionclass, and why it is recommended to throw objects. - What happens when an exception is thrown but not caught: (segfault) → std::terminate()
- Memory scope of the
tryblock: Objects created inside atryblock have a local memory scope. When exiting the block due to an exception or normal execution, stack unwinding occurs, automatically destroying local objects. To preserve them, use pointers and dynamic memory. - Classes inheriting from
std::exceptionand types of errors. - How to configure custom exceptions within classes by creating nested classes that inherit from
std::exceptionand overriding thewhat()method for polymorphism. Why it is not a good idea to convert a normal class into an exception. - Why methods overriding
std::exceptionmethods should use thethrow()clause to specify that the method will not throw another exception. - Nested classes are not constructed by default when using the constructor of the external class. They must be initialized in methods. Principles of nested class usage: Cohesion Principle and Single Responsibility.
Best Practices
- Constructors that may throw errors due to invalid attribute values should only throw the error. The
tryandcatchshould be handled inmainto avoid partially created objects in memory, which could lead to segmentation faults or undefined behavior. - Methods can have their own
tryandcatchblocks for cleaner code. - Nested
tryblocks are possible but not clean. If an exception is thrown in the outertry, the inner block will not execute, and exceptions will not be lost. - When a base class defines its own errors and has methods overridden with polymorphism in derived classes, it is good practice to:
- Throw base class errors from the base class methods.
- In derived class methods, call the base class method to check for base errors, then handle specific errors in the derived class.
- Throw different types of objects for different errors to avoid confusion in
catchblocks.
- When functions or methods with
tryblocks call other functions or methods with their owntryblocks, exceptions thrown in the lower-level function may be caught there, causing the higher-level code to not behave as expected. In such cases, it is important that thetryandcatchblocks are always in the higher-level function, while the lower-level function should only containthrowstatements ot throw objects of different types for different errors.
C++ casts and static_assert.
- ex00: Char* to Numeric Conversion - Using std::strtod and static_cast for type conversion
- ex01: Serialization and Deserialization - Using
reinterpret_castfor pointer manipulation - ex02: Dynamic Cast - Checking pointers in polymorphic class hierarchies
Generic concepts
- Types of casts in C++:
reinterpret_cast,static_cast,dynamic_cast, andconst_caststatic_cast:- Used for safe conversions between compatible types.
- Respects strict aliasing rules.
- Useful for upcasting and downcasting in class hierarchies without virtual functions.
reinterpret_cast:- Reinterprets the bits of memory regions.
- Does not respect type compatibility and breaks strict aliasing rules.
- Used for:
- Conversions between pointers of different class types.
- Serialization.
- Systems programming.
- Accessing memory-mapped regions.
- Drivers.
- Bootloaders.
- Embedded systems.
dynamic_cast:- Runtime type casting.
- Used for polymorphic class hierarchies with virtual functions.
- Behavior:
- For pointers: Performs downcasting and returns
nullptrif the cast fails. - For references: Throws exceptions of type
std::bad_cast(derived fromstd::exception) if the cast fails.
- For pointers: Performs downcasting and returns
- Serialization and deserialization: standardization across different systems
- Why
uintptr_tcan be used to represent pointers (void*): portability across systems with different architectures, serialization of types - Methods for converting
char*to numeric types and parsing:std::stringstreamfrom<sstream>atoi,atol, andatoffrom<cstdlib>strtol,strtoll, andstrtodfrom<cstdlib>
isfunctions in C++ from<cctype>,<cmath>, and<cfloat>limitsfunctions from<climits>and<limits>- Base conversions
- How to generate random numbers (seed + sequence)
Templates and iterators.
- ex00: Template Functions - Creating functions using templates for generic data types, pointers, and references
- ex01: Iter Function Template - Implementing a template function that accepts generic types, including const and non-const pointers
- ex02: Template Array Class - Creating a template-based array class with const and non-const instantiations, overloaded operators, and exception handling
Generic concepts
- What are templates in C++ and why they are used
- Templates and type deduction:
- Function parameter templates:
- Templates with multiple function parameters.
- Templates with constant values and constant types.
- Specialized templates (specialized data types).
- Function templates (callbacks):
- Explicit declaration: function type (return and arguments are data types or the template).
- Implicit declaration (generic).
- Calling a template function with an argument that is not another template function (template callbacks).
- Memory address templates:
- When a generic data template allows memory addresses to be passed (cannot dereference inside the template function).
- When the template declaration specifies that the generic type is a pointer
function(T* ptr), dereferencing is allowed, but the compiler only accepts pointers. - It is also possible to specify in the template function declaration that it is a reference or a pointer to a reference.
- Const data templates (const-correctness):
- When passing a generic data type (by value) to a template, it does not matter if the template parameter is const or not, or if the argument is const or not. The compiler allows both, because a copy is made and type compatibility is ensured.
- When passing by reference, if the template is a non-const reference (e.g.,
T&) and the argument is const, there will be a type compatibility conflict and the compiler will give an error. If the template is a const reference (e.g.,const T&), it accepts const, non-const, and temporaries without conflict. - When the template parameter is a pointer (e.g.,
T*), passing a const or non-const pointer is allowed; the compiler will add const to the type if needed for compatibility. You can also useconst T*in the template to ensure the pointer cannot modify the value it points to inside the function, or useT* constto ensure the pointer itself cannot change the address it points to.
- Function parameter templates:
- Class templates:
- How to create a class template.
- Method declarations must be in the
.hfile. - Methods that return a template-type object can be declared with return type
Object&orObject<templatealias>&. - When instantiating a template-type object, it must be instantiated with
ObjectName<datatype> alias(constructor). - When we want the template object to have its generic elements as const, it must be declared as
const ObjectName<datatype> alias(constructor).
- Overloading the array index access operator within objects.
- Class hierarchy for inheritance:
std::out_of_range.
STL (Standard Template Library), iterators and some algorithms.
- ex00: - Function template to find a value in any non-associative container using its iterator (tested with all standard containers: construction, method, iteration)
- ex01: Span — Class with an internal container, limited size, methods to add elements, equality operators, and algorithms to find the shortest and longest span (difference) between elements. Includes automatic data generation.
- ex02: CMutantStack — Class inheriting from stack, specialized with an internal instantiable iterator, supporting increment, decrement, equality, comparison, dereference operators, and canonical constructors.
Generic Container Concepts
-
Iterators:
- Iterators are nested objects that contain an attribute which is a pointer to the internal positions of the container's data structure.
- To instantiate them, you must use the object's scope operator:
Object::iterator iterator_name;. - They are used to traverse containers and reference internal positions within the container.
- Containers have the methods
begin()andend(), which return an iterator pointing respectively to the first position of the container and to the position just after the last element of the container. - In practice, they are used like pointers, since they are objects overloaded with ad-hoc polymorphism. Their main operations are:
it++,it--,++it,--it,*itto access the value,itgives the memory address of the iterator object,&(*it)gives the memory address of the content pointed to by the iterator, and they support comparison and equality operations (<,>,>=,<=,==). - When used in template functions (in the return type or function body), you must always use the
typename T::iteratorclause for the iterator, but you do not need to specify it in the function argument if the template expects to receive iterators (type deduction). - Iterators are used to create template functions that can traverse containers regardless of their type and perform generic operations on them; most functions in the
<algorithm>library are based on this principle. This makes them valid for most containers that have iterators, such asstd::vector,std::list,std::deque,std::set,std::map, etc. - When specializing a class by adding a nested iterator class, you should create the iterator using the iterator type of the internal container used by your class. If you don't know the exact type, you can use the pattern
typename ContainerType::iteratorto deduce it. For associative or specialized containers, you can usetypename ContainerType::container_type::iteratorto access the iterator type of the underlying container.
-
Containers:
- Tipos de contenedores: secuenciales, asociativos, asociación.
- Métodos de los contenedores genéricos y métodos específicos de los contenedores.
- Formas de construir los contenedores según su tipo.
- Firmas de acceso a los contenedores (iteradores, acceso aleatorio mediante operadores de sobrecarga y mediante métodos específicos).
- Funcionamiento interno de los operadores de sobrecarga de los contenedores.
-
Algoritmos de la STL:
std::sort,std::find,std::count,std::count_if,std::distance,std::advance,std::next,std::prev,std::min_element,std::max_element, adjacent_find`, etc.
STL containers, parsing, and algorithmic complexity.
- ex00: Bitcoin Exchange - Parsing input data and computing exchange rates
- ex01: RPN - Reverse Polish Notation evaluator using a stack
- ex02: PmergeMe - Merge-insert sort on std::vector and std::deque
Generic concepts
- Parsing structured text (CSV/space-delimited) with robust validation
- Advanced use of
std::stringandstd::stringstreamfor parsing-oriented workflows - Advanced casts and
ctypefunctions for parsing checks and validations
- Advanced use of
- Choosing containers per exercise based on use case and performance optimizations
- Using associative containers (
std::map) for lookups and ordered traversal - Stack-based evaluation for expressions (RPN)
- Using sequential containers (
std::vector,std::deque) for sorting and dynamic resizing
- Using associative containers (
- Algorithmic complexity: comparing container performance and scaling
- Hybrid merge-insert sort and timing comparisons across containers
- Iterator usage and range-based operations with the STL
- https://en.cppreference.com/
- https://cplusplus.com/
- https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines
alcarril - https://github.com/alcarril

