Module 00: C++ Fundamentals
Key Concepts:
- Namespaces
- Classes and Objects
- Member Functions (methods)
- Access Specifiers (public/private)
- Constructors and Destructors
- iostream (cout, cin, cerr)
- std::string
- Initialization Lists
- static and const keywords
Why This Matters
Section titled “Why This Matters”Before diving into syntax, understand WHY these concepts exist:
Why Namespaces?
Section titled “Why Namespaces?”Imagine two libraries both define a function called print(). In C, you’d have a naming conflict. Namespaces solve this by giving each library its own “room” for names.
Why Classes Instead of Structs?
Section titled “Why Classes Instead of Structs?”In C, you have structs (data) and functions (behavior) separately. If you want to ensure a struct is always in a valid state, you have to trust that every programmer uses it correctly.
Classes bundle data AND behavior together, plus they can hide internal details. This is called encapsulation.
Why Private and Public?
Section titled “Why Private and Public?”Think of your bank account. You want people to know your balance (public interface: check balance), but you don’t want them directly modifying the internal ledger (private data).
These three concepts—namespaces, encapsulation, and access control—form the foundation of organized, maintainable C++ code.
Understanding C++ Operators (For Complete Beginners)
Section titled “Understanding C++ Operators (For Complete Beginners)”Before diving into C++ code, let’s understand the operators you’ll see throughout this module:
The << Operator (Stream Insertion)
Section titled “The << Operator (Stream Insertion)”std::cout << "Hello" << 42 << std::endl;<<is called the “stream insertion operator” or “output operator”- It inserts data into an output stream (like
std::cout, which stands for “character output”) - Think of it as an arrow pointing in the direction of data flow: data flows FROM the variables TO the output
- You can chain multiple
<<operators together to output multiple values std::endlinserts a newline and flushes the buffer
The >> Operator (Stream Extraction)
Section titled “The >> Operator (Stream Extraction)”std::cin >> number;>>is called the “stream extraction operator” or “input operator”- It extracts data from an input stream (like
std::cin, which stands for “character input”) - Think of it as an arrow pointing in the direction of data flow: data flows FROM the input TO the variable
The :: Operator (Scope Resolution)
Section titled “The :: Operator (Scope Resolution)”std::cout // Access cout from the std namespaceClassName::method // Access method from ClassName::is the “scope resolution operator”- It tells the compiler WHERE to look for a name (variable, function, class)
std::coutmeans “use thecoutthat belongs to thestdnamespace”- This prevents naming conflicts when different libraries use the same names
The . Operator (Member Access)
Section titled “The . Operator (Member Access)”object.method() // Access method of an objectobject.attribute // Access attribute of an object.is the “member access operator” or “dot operator”- It accesses members (methods or attributes) of an object
- Use this when you have an actual object (not a pointer)
The -> Operator (Pointer Member Access)
Section titled “The -> Operator (Pointer Member Access)”pointer->method() // Same as (*pointer).method()pointer->attribute // Same as (*pointer).attribute->is the “arrow operator” or “pointer member access operator”- It’s a shortcut for dereferencing a pointer AND accessing a member
pointer->method()is exactly the same as(*pointer).method()- Use this when you have a pointer to an object
The & Operator (Address-of)
Section titled “The & Operator (Address-of)”int* ptr = &x; // ptr stores the memory address of x&before a variable gives you its memory address- This is how you get a pointer to a variable
- Don’t confuse this with
&in declarations (which creates a reference)
The * Operator (Dereference)
Section titled “The * Operator (Dereference)”*ptr = 42; // Store 42 at the address ptr points toint value = *ptr; // Get the value stored at ptr*before a pointer “dereferences” it (accesses the value at that address)- It follows the pointer to the actual data stored in memory
From C to C++: The Mindset Shift
Section titled “From C to C++: The Mindset Shift”What Changes?
Section titled “What Changes?”| C | C++ |
|---|---|
printf() | std::cout << |
scanf() | std::cin >> |
malloc()/free() | new/delete |
struct (data only) | class (data + behavior) |
| Functions operate on data | Objects have methods |
The Forbidden List (42 Rules)
Section titled “The Forbidden List (42 Rules)”// FORBIDDEN - will get you 0printf("Hello"); // Forbidden: *printf(), use std::cout insteadmalloc(sizeof(int)); // Forbidden: *alloc() (malloc/calloc/realloc), use new insteadfree(ptr); // Forbidden, use delete instead
// FORBIDDEN - will get you -42using namespace std; // Must prefix with std::friend class Other; // Unless explicitly allowed by subject
// FORBIDDEN in Modules 00-07 - will get you -42#include <vector> // No STL containers before Module 08#include <list> // No STL containers before Module 08#include <map> // No STL containers before Module 08#include <algorithm> // No STL algorithms before Module 08Namespaces
Section titled “Namespaces”Why Namespaces Exist
Section titled “Why Namespaces Exist”Namespaces solve two problems:
1. Name collisions: In large projects, two libraries might define a function with the same name.
// Without namespaces - collision!void print(); // Library Avoid print(); // Library B - ERROR!
// With namespaces - no collisionnamespace LibraryA { void print();}namespace LibraryB { void print();}
// UsageLibraryA::print();LibraryB::print();2. Code organization: Namespaces let you group related symbols semantically across multiple files. Unlike C where organization is file-based, namespaces let you organize by meaning. A namespace can span many files, and related functions stay together logically even when physically separated.
The std Namespace
Section titled “The std Namespace”Everything from the C++ standard library lives in std:
std::cout // output streamstd::cin // input streamstd::cerr // error streamstd::string // string classstd::endl // newline + flushThe :: Operator (Scope Resolution)
Section titled “The :: Operator (Scope Resolution)”std::cout // cout from std namespace::globalFunction() // function from global namespace (no namespace)ClassName::method // method from ClassNameiostream: Input/Output Streams
Section titled “iostream: Input/Output Streams”Basic Output with cout
Section titled “Basic Output with cout”#include <iostream>
int main() { std::cout << "Hello, World!" << std::endl;
int x = 42; std::cout << "The answer is " << x << std::endl;
// Chaining multiple values std::cout << "a=" << 1 << ", b=" << 2 << std::endl;
return 0;}Basic Input with cin
Section titled “Basic Input with cin”#include <iostream>#include <string>
int main() { int number; std::cout << "Enter a number: "; std::cin >> number;
std::string name; std::cout << "Enter your name: "; std::cin >> name; // Only reads until whitespace!
// For full line input: std::getline(std::cin, name);
return 0;}Important: cin quirks
Section titled “Important: cin quirks”// PROBLEM: mixing cin >> and getlineint age;std::string name;
std::cin >> age; // Leaves '\n' in bufferstd::getline(std::cin, name); // Reads empty line!
// SOLUTION: clear the bufferstd::cin >> age;std::cin.ignore(); // Ignore the leftover '\n'std::getline(std::cin, name);Output Formatting with iomanip
Section titled “Output Formatting with iomanip”#include <iostream>#include <iomanip>
int main() { // Set field width std::cout << std::setw(10) << "Hello" << std::endl; // " Hello"
// Right/left alignment std::cout << std::right << std::setw(10) << "Hi" << std::endl; // " Hi" std::cout << std::left << std::setw(10) << "Hi" << std::endl; // "Hi "
// Fill character std::cout << std::setfill('.') << std::setw(10) << "Hi" << std::endl; // "........Hi"
return 0;}std::string
Section titled “std::string”Why std::string over char*?
Section titled “Why std::string over char*?”// C-style (dangerous, manual memory)char* str = (char*)malloc(100);strcpy(str, "Hello");// Must remember to free!
// C++ style (safe, automatic)std::string str = "Hello";// Memory managed automaticallyBasic Operations
Section titled “Basic Operations”#include <string>
std::string s = "Hello";
// Lengths.length(); // 5s.size(); // 5 (same thing)
// Access characterss[0]; // 'H's.at(0); // 'H' (with bounds checking)
// Concatenations + " World"; // "Hello World"s.append(" World"); // Modifies s
// Comparisons == "Hello"; // trues < "World"; // true (lexicographic)
// Substringss.substr(0, 3); // "Hel"s.substr(2); // "llo"
// Finds.find("ll"); // 2 (index)s.find("xyz"); // std::string::npos (not found)
// Replace (but remember: forbidden in ex04!)s.replace(0, 2, "YY"); // "YYllo"
// Clears.empty(); // falses.clear(); // s is now ""Iteration
Section titled “Iteration”std::string s = "Hello";
// Index-basedfor (size_t i = 0; i < s.length(); i++) { std::cout << s[i];}
// C++98 doesn't have range-based for loops!// This is C++11: for (char c : s) { } // FORBIDDENClasses and Objects
Section titled “Classes and Objects”Class vs Struct: What’s the Difference?
Section titled “Class vs Struct: What’s the Difference?”In C++, both class and struct can have member variables and functions. The only difference is the default access specifier:
| Feature | class | struct |
|---|---|---|
| Default access | private | public |
| Default inheritance | private | public |
| Members accessible? | Only inside class | From anywhere |
Practical Example:
class MyClass { int x; // PRIVATE by default - only accessible inside class void foo(); // PRIVATE by default};
struct MyStruct { int x; // PUBLIC by default - accessible from anywhere void foo(); // PUBLIC by default};
MyClass c;c.x = 5; // ERROR: x is private!
MyStruct s;s.x = 5; // OK: x is publicWhen to use each:
- Use
classwhen you need encapsulation (hide implementation) - Use
structfor simple data structures (like C structs) - In practice, both work the same - it’s about convention
Visual Memory Layout:
MyClass object: MyStruct object:+-------------+ +-------------+| _firstName | | _firstName | <-- All members| _lastName | | _lastName | are accessible| _phone | | _phone |+-------------+ +-------------+ (private) (public)Basic Class Structure
Section titled “Basic Class Structure”#ifndef CONTACT_HPP#define CONTACT_HPP
#include <string>
class Contact {private: // Attributes (data members) std::string _firstName; std::string _lastName; std::string _phoneNumber;
public: // Constructor Contact();
// Destructor ~Contact();
// Member functions (methods) void setFirstName(std::string name); std::string getFirstName() const; void display() const;};
#endif#include "Contact.hpp"#include <iostream>
// Constructor implementationContact::Contact() { std::cout << "Contact created" << std::endl;}
// Destructor implementationContact::~Contact() { std::cout << "Contact destroyed" << std::endl;}
// Settervoid Contact::setFirstName(std::string name) { this->_firstName = name;}
// Getter (const - doesn't modify object)std::string Contact::getFirstName() const { return this->_firstName;}
// Display methodvoid Contact::display() const { std::cout << "Name: " << _firstName << " " << _lastName << std::endl;}Access Specifiers
Section titled “Access Specifiers”| Specifier | Access |
|---|---|
private | Only accessible within the class |
public | Accessible from anywhere |
protected | Accessible in class and derived classes |
Rule of thumb: Make attributes private, provide public getters/setters.
The this Pointer
Section titled “The this Pointer”Every non-static member function has access to a special pointer called this. It points to the current instance - the object on which the method was called. This is how member functions know which object’s data to access.
class Example {private: int value;
public: void setValue(int value) { // 'value' refers to parameter // 'this->value' refers to member this->value = value; }
// this is implicit - these two are equivalent: int getValue() { return value; } int getValue() { return this->value; }};Why this matters: When you call obj.setValue(42), the compiler passes &obj as a hidden first argument. Inside the function, this == &obj.
const Member Functions
Section titled “const Member Functions”class Example {private: int _value;
public: // Can modify object void setValue(int v) { _value = v; }
// Cannot modify object (const at the end) int getValue() const { return _value; }};Constructors and Destructors
Section titled “Constructors and Destructors”Default Constructor
Section titled “Default Constructor”class MyClass {public: MyClass() { std::cout << "Default constructor called" << std::endl; }};
// UsageMyClass obj; // Calls default constructorParameterized Constructor
Section titled “Parameterized Constructor”class Contact {private: std::string _name; int _age;
public: Contact(std::string name, int age) { _name = name; _age = age; }};
// UsageContact c("John", 25);Initialization Lists (IMPORTANT!)
Section titled “Initialization Lists (IMPORTANT!)”class Contact {private: std::string _name; int _age;
public: // WITHOUT initialization list (assignment in body) Contact(std::string name, int age) { _name = name; // First default-constructed, then assigned _age = age; }
// WITH initialization list (direct initialization) Contact(std::string name, int age) : _name(name), _age(age) { // Members initialized before body executes }};Why use initialization lists?
-
More efficient: Without init list, members are default-constructed THEN assigned (2 operations). With init list, members are directly initialized (1 operation).
// WITHOUT init list - TWO operations per member:Contact(std::string name) {// 1. _name is default-constructed (empty string)// 2. _name is assigned the value of 'name'_name = name;}// WITH init list - ONE operation per member:Contact(std::string name) : _name(name) {// _name is directly constructed with 'name'} -
Required for
constmembers (can’t assign to const after construction) -
Required for reference members (must be bound at initialization)
-
Required for members without default constructors
class Example {private: const int _id; // MUST use init list std::string& _ref; // MUST use init list
public: // This is the ONLY way: Example(int id, std::string& ref) : _id(id), _ref(ref) {}};Destructor
Section titled “Destructor”class FileHandler {private: int* _data;
public: FileHandler() { _data = new int[100]; // Allocate }
~FileHandler() { delete[] _data; // Clean up std::cout << "FileHandler destroyed, memory freed" << std::endl; }};Static Members
Section titled “Static Members”Static Attributes
Section titled “Static Attributes”Shared across ALL instances of a class.
// Headerclass Counter {private: static int _count; // Declaration
public: Counter() { _count++; } ~Counter() { _count--; } static int getCount() { return _count; }};
// Source (MUST define outside class)int Counter::_count = 0; // Definition + initialization
// UsageCounter a;Counter b;Counter c;std::cout << Counter::getCount(); // 3Static Member Functions
Section titled “Static Member Functions”Can be called without an object. Cannot access non-static members.
class Math {public: static int add(int a, int b) { return a + b; }};
// Usage - no object neededint result = Math::add(5, 3);const Keyword
Section titled “const Keyword”Design Philosophy: const is more than a language feature - it’s a discipline for writing correct code. By marking everything that shouldn’t change as const, you:
- Catch bugs at compile time (accidental modifications)
- Document intent (readers know what won’t change)
- Enable compiler optimizations
- Make code easier to reason about in complex systems
Rule of thumb: Use const by default. Only remove it when you need to modify something.
const Variables
Section titled “const Variables”const int MAX = 100; // Cannot be modifiedMAX = 200; // ERROR!const Parameters
Section titled “const Parameters”void print(const std::string& s) { // s cannot be modified // Passed by reference (efficient, no copy) std::cout << s << std::endl;}const Member Functions
Section titled “const Member Functions”class Example {public: int getValue() const { // Promises not to modify object return _value; }};const Return Values
Section titled “const Return Values”class Example {private: std::string _name;
public: // Returns const reference - caller cannot modify const std::string& getName() const { return _name; }};Include Guards
Section titled “Include Guards”Every header file MUST have include guards to prevent double inclusion:
#ifndef CONTACT_HPP#define CONTACT_HPP
class Contact { // ...};
#endifWhy?
// Without guards:#include "Contact.hpp"#include "Contact.hpp" // ERROR: Contact redefined!
// With guards:#include "Contact.hpp" // Defines CONTACT_HPP, includes class#include "Contact.hpp" // CONTACT_HPP already defined, skippedCompilation
Section titled “Compilation”Makefile Basics for C++
Section titled “Makefile Basics for C++”NAME = programCXX = c++CXXFLAGS = -Wall -Wextra -Werror -std=c++98
SRCS = main.cpp Contact.cpp PhoneBook.cppOBJS = $(SRCS:.cpp=.o)
all: $(NAME)
$(NAME): $(OBJS) $(CXX) $(CXXFLAGS) -o $(NAME) $(OBJS)
%.o: %.cpp $(CXX) $(CXXFLAGS) -c $< -o $@
clean: rm -f $(OBJS)
fclean: clean rm -f $(NAME)
re: fclean all
.PHONY: all clean fclean reThis structure also avoids “relinking”: if none of the .cpp files changed, make will not re-run the final link step.
Line-by-Line Makefile Explanation
Section titled “Line-by-Line Makefile Explanation”| Line | Code | Explanation |
|---|---|---|
| 1 | NAME = program | Defines a variable NAME containing the final executable name. Using a variable makes it easy to change the program name in one place. |
| 2 | CXX = c++ | Defines the C++ compiler to use. c++ is the standard command that typically links to g++ or clang++. Using a variable allows easy compiler switching. |
| 3 | CXXFLAGS = -Wall -Wextra -Werror -std=c++98 | Compiler flags: -Wall enables all common warnings, -Wextra enables extra warnings, -Werror treats warnings as errors (compilation fails on any warning), -std=c++98 enforces C++98 standard (required by 42). |
| 5 | SRCS = main.cpp Contact.cpp PhoneBook.cpp | Lists all source files to compile. Each .cpp file will be compiled into its own object file. |
| 6 | OBJS = $(SRCS:.cpp=.o) | Substitution reference: Takes the SRCS list and replaces .cpp with .o. Result: main.o Contact.o PhoneBook.o. This creates the list of object files. |
| 8 | all: $(NAME) | The default target. When you run make without arguments, it builds all, which depends on $(NAME) (the executable). |
| 10-11 | $(NAME): $(OBJS) | Link rule: The executable depends on all object files. If any .o file is newer than the executable, this rule runs. |
| 11 | $(CXX) $(CXXFLAGS) -o $(NAME) $(OBJS) | The linking command: calls the compiler with flags, -o $(NAME) specifies output filename, $(OBJS) lists all object files to link together. |
| 13-14 | %.o: %.cpp | Pattern rule: Defines how to build ANY .o file from its corresponding .cpp file. The % is a wildcard that matches any filename. |
| 14 | $(CXX) $(CXXFLAGS) -c $< -o $@ | The compilation command: -c means compile only (don’t link), $< is an automatic variable representing the first prerequisite (the .cpp file), $@ is the target (the .o file). |
| 16-17 | clean: | A target to remove object files. Has no prerequisites, so it always runs when called. |
| 17 | rm -f $(OBJS) | Removes all object files. -f means “force” (don’t error if files don’t exist). |
| 19-20 | fclean: clean | ”Full clean”: first runs clean (its prerequisite), then removes the executable too. |
| 20 | rm -f $(NAME) | Removes the final executable. |
| 22 | re: fclean all | ”Remake”: runs fclean first, then all. This forces a complete recompilation from scratch. |
| 24 | .PHONY: all clean fclean re | Declares these targets as “phony” (not actual files). Without this, if a file named clean existed, make clean wouldn’t run the rule. |
Automatic Variables Quick Reference
Section titled “Automatic Variables Quick Reference”| Variable | Meaning |
|---|---|
$@ | The target of the rule (what we’re building) |
$< | The first prerequisite (first dependency) |
$^ | All prerequisites (all dependencies) |
Why This Structure?
Section titled “Why This Structure?”- Incremental compilation: Only recompiles files that changed
- No relinking: If no object files changed, the executable isn’t rebuilt
- Easy maintenance: Change
NAMEorSRCSin one place - Clean targets:
clean,fclean, andrefollow 42’s standard conventions
EOF Handling with std::getline
Section titled “EOF Handling with std::getline”When reading input in a loop, you need to handle EOF (Ctrl+D on Unix, Ctrl+Z on Windows):
std::string line;
// std::getline returns the stream, which converts to false on EOFwhile (std::getline(std::cin, line)) { // Process line std::cout << "Got: " << line << std::endl;}// Loop exits when EOF is reached
// You can also check explicitly:if (std::cin.eof()) { std::cout << "End of input reached" << std::endl;}Graceful Exit on EOF
Section titled “Graceful Exit on EOF”#include <cstdlib> // for std::exit
std::string input;std::cout << "Enter value: ";if (!std::getline(std::cin, input)) { std::cout << std::endl; // Print newline for clean output std::exit(0); // Exit program gracefully}Circular Buffer Pattern
Section titled “Circular Buffer Pattern”When you need a fixed-size collection where new items replace the oldest:
class PhoneBook {private: Contact _contacts[8]; // Fixed size array int _index; // Next position to write int _count; // Total contacts stored
public: PhoneBook() : _index(0), _count(0) {}
void addContact(const Contact& c) { _contacts[_index] = c;
// Wrap around using modulo _index = (_index + 1) % 8; // 0,1,2,3,4,5,6,7,0,1,2...
// Track count up to max if (_count < 8) _count++; }};How Modulo Wrapping Works
Section titled “How Modulo Wrapping Works”int index = 0;int size = 8;
index = (index + 1) % size; // 0 -> 1index = (index + 1) % size; // 1 -> 2// ... after 7:index = (index + 1) % size; // 7 -> 0 (wraps around!)Pointers to Members
Section titled “Pointers to Members”C++ extends the pointer concept to allow pointers to class members (both attributes and member functions). This is different from regular pointers.
Pointer to Member Attribute
Section titled “Pointer to Member Attribute”class Sample {public: int value;};
// Pointer to member attributeint Sample::*ptr = &Sample::value;
// Usage - need an instance to dereferenceSample s;s.value = 42;std::cout << s.*ptr << std::endl; // Prints 42
Sample* sp = &s;std::cout << sp->*ptr << std::endl; // Also prints 42Pointer to Member Function
Section titled “Pointer to Member Function”class Sample {public: void display() { std::cout << "Hello" << std::endl; } void greet(std::string name) { std::cout << "Hi " << name << std::endl; }};
// Pointer to member function (no parameters)void (Sample::*funcPtr)() = &Sample::display;
// UsageSample s;(s.*funcPtr)(); // Calls s.display()
Sample* sp = &s;(sp->*funcPtr)(); // Also calls display()
// Pointer to member function with parametersvoid (Sample::*greetPtr)(std::string) = &Sample::greet;(s.*greetPtr)("World"); // Calls s.greet("World")Why Pointers to Members?
Section titled “Why Pointers to Members?”- Callbacks: Select which member function to call at runtime
- Data-driven design: Access different attributes based on configuration
- Generic algorithms: Write functions that work on any member
Common Mistakes to Avoid
Section titled “Common Mistakes to Avoid”- Using
using namespace std;- Forbidden at 42 (grade -42) - Using
friendkeyword - Forbidden unless explicitly allowed (grade -42) - Using STL containers/algorithms before Module 08 - Forbidden (grade -42)
- Forgetting include guards - Causes compilation errors (grade 0)
- Putting implementation in headers - Grade 0 (except templates)
- Not ending output with newline - Required by subject
- Using printf/scanf - Forbidden at 42 (grade 0)
- Forgetting
conston getters - Bad practice - Not initializing members - Undefined behavior
- Memory leaks - Always pair
newwithdelete
Exercises
Section titled “Exercises”Now let’s apply these concepts to the module exercises.
Exercise 00: Megaphone
Section titled “Exercise 00: Megaphone”Subject Analysis
Section titled “Subject Analysis”The Megaphone exercise asks you to create a program that:
- Takes command-line arguments and converts them all to uppercase
- Concatenates all arguments without adding spaces between them
- Prints a default message
* LOUD AND UNBEARABLE FEEDBACK NOISE *when no arguments are given
This is your first C++ program. The goal is to learn basic I/O with std::cout and understand how command-line arguments work in C++.
Approach Strategy
Section titled “Approach Strategy”Before writing any code, think through the problem:
- Understand argc/argv:
argcis the argument count,argvis an array of C-strings.argv[0]is always the program name. - Handle the edge case first: Check if no arguments were given (argc == 1).
- Process each argument: Loop from index 1 to skip the program name.
- Convert character by character: For each argument, loop through each character and convert to uppercase.
- Output at the end: Print a single newline after all characters.
Progressive Code Building
Section titled “Progressive Code Building”Stage 1 - Basic skeleton:
#include <iostream>
int main(int argc, char **argv){ // TODO: implement return 0;}Stage 2 - Handle no-args case:
#include <iostream>
int main(int argc, char **argv){ if (argc == 1) { std::cout << "* LOUD AND UNBEARABLE FEEDBACK NOISE *" << std::endl; return 0; } // TODO: handle arguments return 0;}Stage 3 - Complete implementation:
#include <iostream>#include <cctype>
int main(int argc, char **argv){ if (argc == 1) { std::cout << "* LOUD AND UNBEARABLE FEEDBACK NOISE *" << std::endl; return 0; }
for (int i = 1; i < argc; i++) { for (int j = 0; argv[i][j]; j++) { std::cout << (char)toupper(argv[i][j]); } } std::cout << std::endl;
return 0;}Line-by-Line Explanation
Section titled “Line-by-Line Explanation”| Line | Code | Why |
|---|---|---|
| 1 | #include <iostream> | C++ I/O library. Use this instead of stdio.h. Gives us std::cout. |
| 2 | #include <cctype> | C++ wrapper for character functions. Provides toupper(). |
| 4 | int main(int argc, char **argv) | Standard signature for programs receiving command-line arguments. |
| 6 | if (argc == 1) | Why 1, not 0? Because argv[0] is always the program name, so 1 means “no actual arguments”. |
| 8 | std::cout << ... << std::endl; | Stream the message to stdout. std::endl flushes the buffer and adds newline. |
| 9 | return 0; | Exit early after printing the default message. |
| 12 | for (int i = 1; i < argc; i++) | Start at 1 to skip argv[0] (the program name). |
| 14 | for (int j = 0; argv[i][j]; j++) | Loop until null terminator. argv[i][j] is the j-th character of i-th argument. |
| 16 | (char)toupper(argv[i][j]) | toupper() returns an int, so we cast to char for proper output. |
| 19 | std::cout << std::endl; | Print final newline only after all characters are output. |
Common Pitfalls
Section titled “Common Pitfalls”-
Using printf instead of std::cout
- C habit! In C++ modules, you must use
std::coutfor output. - Wrong:
printf("hello"); - Right:
std::cout << "hello";
- C habit! In C++ modules, you must use
-
Forgetting that toupper() returns int
- Without the cast, you may print ASCII codes instead of characters.
- Wrong:
std::cout << toupper(c); - Right:
std::cout << (char)toupper(c);
-
Forgetting the no-argument case
- The subject explicitly requires printing
* LOUD AND UNBEARABLE FEEDBACK NOISE *when there are no arguments. - Always test with
./megaphone(no args).
- The subject explicitly requires printing
-
Adding spaces between arguments
- The subject wants arguments concatenated directly.
./megaphone "Hello" "World"should outputHELLOWORLD, notHELLO WORLD.
Testing Tips
Section titled “Testing Tips”Test your program with the exact examples from the subject:
./megaphone "shhhhh... I think the students are asleep..."# Expected: SHHHHH... I THINK THE STUDENTS ARE ASLEEP...
./megaphone Damnit " ! " "Sorry students, I thought this thing was off."# Expected: DAMNIT ! SORRY STUDENTS, I THOUGHT THIS THING WAS OFF.
./megaphone# Expected: * LOUD AND UNBEARABLE FEEDBACK NOISE *Additional edge cases to verify:
./megaphone ""# Expected: (empty line - just a newline)
./megaphone "123" "!@#"# Expected: 123!@# (non-letters pass through unchanged)Final Code
Section titled “Final Code”#include <iostream>#include <cctype>
int main(int argc, char **argv){ if (argc == 1) { std::cout << "* LOUD AND UNBEARABLE FEEDBACK NOISE *" << std::endl; return 0; }
for (int i = 1; i < argc; i++) { for (int j = 0; argv[i][j]; j++) { std::cout << (char)toupper(argv[i][j]); } } std::cout << std::endl;
return 0;}Exercise 01: PhoneBook
Section titled “Exercise 01: PhoneBook”Your first real C++ program with classes. This exercise teaches you object-oriented fundamentals: encapsulation, class design, and how objects interact.
Subject Analysis
Section titled “Subject Analysis”The PhoneBook exercise requires you to build a contact management system with these constraints:
Two classes needed:
- Contact: Stores data for a single person (first name, last name, nickname, phone number, darkest secret)
- PhoneBook: Manages a collection of up to 8 contacts
Core requirements:
- Fixed array of 8 contacts - NO vectors, NO dynamic allocation (
new/delete) - Three commands:
ADD,SEARCH,EXIT - Circular buffer: When full, oldest contact gets replaced
- No empty fields: Every field must have content
- SEARCH display: 10-character columns, right-aligned, truncate with ”.” if too long
- Recommended: handle EOF (Ctrl+D) gracefully (treat it like
EXIT)
The SEARCH command has two phases:
- Display a formatted table showing all contacts
- Prompt for an index, then display that contact’s full details
Approach Strategy
Section titled “Approach Strategy”Think through the design before coding:
Step 1 - Design the Contact class first
Ask yourself: What data does a contact hold? What methods does it need?
- 5 private string attributes (the fields)
- A flag to know if the slot is used (
_isEmpty) - Setters to store data, getters to retrieve it
- Everything private except the interface methods
Step 2 - Design the PhoneBook class
Ask yourself: How do I manage 8 contacts? How do I know where to add next?
- Fixed array:
Contact _contacts[8] - Track where to add next:
_currentIndex(cycles 0→7→0) - Track how many exist:
_totalContacts(caps at 8)
Step 3 - Plan the main loop
while (true) { read command if ADD → call phonebook.addContact() if SEARCH → call phonebook.searchContacts() if EXIT → break}Step 4 - Handle edge cases
- Empty input rejection (loop until non-empty)
- EOF handling (
std::getlinereturns false) - Invalid index (check range and format)
Key insight - The circular buffer:
You need two trackers:
_currentIndex: Where to add the NEXT contact (wraps with modulo 8)_totalContacts: How many contacts exist (maximum 8)
When adding contact #9, _currentIndex will be 0, replacing the oldest contact.
Progressive Code Building
Section titled “Progressive Code Building”Stage 1 - Contact Class (the data container):
#ifndef CONTACT_HPP#define CONTACT_HPP
#include <string>
class Contact {private: std::string _firstName; std::string _lastName; std::string _nickname; std::string _phoneNumber; std::string _darkestSecret; bool _isEmpty;
public: Contact(); ~Contact();
// Setters - take const reference for efficiency void setFirstName(const std::string& firstName); void setLastName(const std::string& lastName); void setNickname(const std::string& nickname); void setPhoneNumber(const std::string& phoneNumber); void setDarkestSecret(const std::string& secret); void setIsEmpty(bool isEmpty);
// Getters - marked const (promise not to modify object) std::string getFirstName() const; std::string getLastName() const; std::string getNickname() const; std::string getPhoneNumber() const; std::string getDarkestSecret() const; bool isEmpty() const;};
#endifWhy these design decisions?
| Decision | Reason |
|---|---|
| Private attributes | Encapsulation - external code can’t corrupt data |
const std::string& params | Avoids copying the string, more efficient |
const on getters | Promises these methods won’t modify the object |
_isEmpty flag | Lets us check if a contact slot is actually used |
| Underscore prefix | Common convention to identify private members |
Stage 2 - PhoneBook Class (the manager):
#ifndef PHONEBOOK_HPP#define PHONEBOOK_HPP
#include "Contact.hpp"#include <string>
class PhoneBook {private: Contact _contacts[8]; // Fixed array - NO vectors! int _currentIndex; // Where to add next (0-7, wraps) int _totalContacts; // How many stored (max 8)
// Private helper methods - internal implementation details std::string _truncate(const std::string& str) const; void _displayContactRow(int index) const; std::string _getInput(const std::string& prompt) const;
public: PhoneBook(); ~PhoneBook();
void addContact(); void searchContacts() const;};
#endifWhy Contact _contacts[8] and not a vector?
The subject explicitly forbids containers from the STL. This forces you to understand how fixed-size arrays work in C++. The array is allocated on the stack as part of the PhoneBook object itself.
Stage 3 - The Circular Buffer Logic:
void PhoneBook::addContact() { Contact newContact;
// Collect all fields (helper rejects empty input) newContact.setFirstName(_getInput("Enter first name: ")); newContact.setLastName(_getInput("Enter last name: ")); newContact.setNickname(_getInput("Enter nickname: ")); newContact.setPhoneNumber(_getInput("Enter phone number: ")); newContact.setDarkestSecret(_getInput("Enter darkest secret: ")); newContact.setIsEmpty(false);
// Store at current index (overwrites oldest if full) _contacts[_currentIndex] = newContact;
// Circular buffer: wrap around using modulo _currentIndex = (_currentIndex + 1) % 8;
// Track count (cap at 8) if (_totalContacts < 8) _totalContacts++;
std::cout << "Contact added successfully!" << std::endl;}The modulo trick explained:
(_currentIndex + 1) % 8 makes the index cycle: 0→1→2→3→4→5→6→7→0→1→2…
_currentIndex | + 1 | % 8 | Result |
|---|---|---|---|
| 0 | 1 | 1 % 8 | 1 |
| 6 | 7 | 7 % 8 | 7 |
| 7 | 8 | 8 % 8 | 0 ← wraps! |
Stage 4 - SEARCH Display Formatting:
std::string PhoneBook::_truncate(const std::string& str) const { if (str.length() > 10) return str.substr(0, 9) + "."; // 9 chars + dot = 10 return str;}
void PhoneBook::_displayContactRow(int index) const { std::cout << std::setw(10) << std::right << index << "|"; std::cout << std::setw(10) << std::right << _truncate(_contacts[index].getFirstName()) << "|"; std::cout << std::setw(10) << std::right << _truncate(_contacts[index].getLastName()) << "|"; std::cout << std::setw(10) << std::right << _truncate(_contacts[index].getNickname()) << std::endl;}Understanding std::setw and std::right:
std::setw(10)- Set width to 10 characters for the next outputstd::right- Align content to the right within that width
Example: "John" becomes " John" (6 spaces + 4 chars = 10)
Line-by-Line Explanation
Section titled “Line-by-Line Explanation”Input validation with EOF handling:
std::string PhoneBook::_getInput(const std::string& prompt) const { std::string input;
while (true) { std::cout << prompt; if (!std::getline(std::cin, input)) { // Returns false on EOF std::cout << std::endl; std::exit(0); // Clean exit on Ctrl+D } if (!input.empty()) break; std::cout << "Field cannot be empty. Please try again." << std::endl; } return input;}| Line | Why |
|---|---|
std::getline(std::cin, input) | Reads entire line including spaces (unlike std::cin >>) |
if (!std::getline(...)) | getline returns false when EOF (Ctrl+D) is encountered |
std::exit(0) | Terminates program cleanly - required for EOF handling |
if (!input.empty()) | Rejects empty strings, keeps prompting |
Index validation for SEARCH:
// Get and validate indexstd::string indexStr;std::cout << "Enter index to view details: ";if (!std::getline(std::cin, indexStr)) { std::cout << std::endl; return;}
// Strict validation: must be single digit 0-7if (indexStr.length() != 1 || indexStr[0] < '0' || indexStr[0] > '7') { std::cout << "Invalid index." << std::endl; return;}
int index = indexStr[0] - '0'; // Convert char digit to intif (index >= _totalContacts) { std::cout << "Invalid index." << std::endl; return;}| Line | Why |
|---|---|
indexStr.length() != 1 | Only accept single character input |
indexStr[0] < '0' || indexStr[0] > '7' | Must be digit 0-7 |
indexStr[0] - '0' | Character to int conversion: '5' - '0' = 5 |
index >= _totalContacts | Can’t access contact that doesn’t exist |
Common Pitfalls
Section titled “Common Pitfalls”1. Using vectors or dynamic allocation
The subject EXPLICITLY forbids this. You will fail the evaluation.
// WRONG - forbiddenstd::vector<Contact> _contacts;Contact* _contacts = new Contact[8];
// RIGHT - fixed arrayContact _contacts[8];2. Wrong display formatting
// WRONG - no formattingstd::cout << firstName << "|" << lastName << std::endl;
// RIGHT - 10 chars, right-aligned, truncatedstd::cout << std::setw(10) << std::right;if (firstName.length() > 10) std::cout << firstName.substr(0, 9) + ".";else std::cout << firstName;3. Not handling the circular buffer correctly
// WRONG - just keeps incrementing_currentIndex++;
// RIGHT - wraps around_currentIndex = (_currentIndex + 1) % 8;4. Accepting empty fields
// WRONG - accepts anythingstd::getline(std::cin, input);contact.setFirstName(input);
// RIGHT - loop until non-emptywhile (input.empty()) { std::cout << "Field cannot be empty: "; std::getline(std::cin, input);}5. Not handling EOF (Ctrl+D)
// WRONG - ignores EOF, program hangsstd::getline(std::cin, input);
// RIGHT - check return value and exitif (!std::getline(std::cin, input)) { std::cout << std::endl; std::exit(0);}6. Making attributes public
// WRONG - breaks encapsulationclass Contact {public: std::string firstName; // Anyone can modify directly!};
// RIGHT - private with accessorsclass Contact {private: std::string _firstName;public: void setFirstName(const std::string& name); std::string getFirstName() const;};Testing Tips
Section titled “Testing Tips”# Test ADD functionality> ADDEnter first name: JohnEnter last name: DoeEnter nickname: JDEnter phone number: 555-1234Enter darkest secret: Likes pineapple pizzaContact added successfully!
# Test empty field rejection> ADDEnter first name:Field cannot be empty. Please try again.Enter first name: Jane
# Test SEARCH with no contacts> SEARCHPhoneBook is empty.
# Test SEARCH display formatting> SEARCH Index|First Name| Last Name| Nickname-------------------------------------------- 0| John| Doe| JDEnter index to view details: 0First Name: JohnLast Name: DoeNickname: JDPhone Number: 555-1234Darkest Secret: Likes pineapple pizza
# Test truncation (name > 10 chars)# Add contact with first name "Christopher"> SEARCH# Should display "Christoph." (9 chars + dot)
# Test circular buffer (add 9 contacts)# Contact #9 should replace contact #0
# Test invalid index> SEARCHEnter index to view details: 9Invalid index.
> SEARCHEnter index to view details: abcInvalid index.
# Test EOF handling> (Press Ctrl+D)# Program should exit cleanly with newlineFinal Code
Section titled “Final Code”Contact.hpp
#ifndef CONTACT_HPP#define CONTACT_HPP
#include <string>
class Contact {private: std::string _firstName; std::string _lastName; std::string _nickname; std::string _phoneNumber; std::string _darkestSecret; bool _isEmpty;
public: Contact(); ~Contact();
void setFirstName(const std::string& firstName); void setLastName(const std::string& lastName); void setNickname(const std::string& nickname); void setPhoneNumber(const std::string& phoneNumber); void setDarkestSecret(const std::string& secret); void setIsEmpty(bool isEmpty);
std::string getFirstName() const; std::string getLastName() const; std::string getNickname() const; std::string getPhoneNumber() const; std::string getDarkestSecret() const; bool isEmpty() const;};
#endifContact.cpp
#include "Contact.hpp"
Contact::Contact() : _isEmpty(true) {}
Contact::~Contact() {}
void Contact::setFirstName(const std::string& firstName) { _firstName = firstName; }void Contact::setLastName(const std::string& lastName) { _lastName = lastName; }void Contact::setNickname(const std::string& nickname) { _nickname = nickname; }void Contact::setPhoneNumber(const std::string& phoneNumber) { _phoneNumber = phoneNumber; }void Contact::setDarkestSecret(const std::string& secret) { _darkestSecret = secret; }void Contact::setIsEmpty(bool isEmpty) { _isEmpty = isEmpty; }
std::string Contact::getFirstName() const { return _firstName; }std::string Contact::getLastName() const { return _lastName; }std::string Contact::getNickname() const { return _nickname; }std::string Contact::getPhoneNumber() const { return _phoneNumber; }std::string Contact::getDarkestSecret() const { return _darkestSecret; }bool Contact::isEmpty() const { return _isEmpty; }PhoneBook.hpp
#ifndef PHONEBOOK_HPP#define PHONEBOOK_HPP
#include "Contact.hpp"#include <string>
class PhoneBook {private: Contact _contacts[8]; int _currentIndex; int _totalContacts;
std::string _truncate(const std::string& str) const; void _displayContactRow(int index) const; std::string _getInput(const std::string& prompt) const;
public: PhoneBook(); ~PhoneBook();
void addContact(); void searchContacts() const;};
#endifPhoneBook.cpp
#include "PhoneBook.hpp"#include <iostream>#include <iomanip>#include <cstdlib>
PhoneBook::PhoneBook() : _currentIndex(0), _totalContacts(0) {}
PhoneBook::~PhoneBook() {}
std::string PhoneBook::_truncate(const std::string& str) const { if (str.length() > 10) return str.substr(0, 9) + "."; return str;}
void PhoneBook::_displayContactRow(int index) const { std::cout << std::setw(10) << std::right << index << "|"; std::cout << std::setw(10) << std::right << _truncate(_contacts[index].getFirstName()) << "|"; std::cout << std::setw(10) << std::right << _truncate(_contacts[index].getLastName()) << "|"; std::cout << std::setw(10) << std::right << _truncate(_contacts[index].getNickname()) << std::endl;}
std::string PhoneBook::_getInput(const std::string& prompt) const { std::string input;
while (true) { std::cout << prompt; if (!std::getline(std::cin, input)) { std::cout << std::endl; std::exit(0); } if (!input.empty()) break; std::cout << "Field cannot be empty. Please try again." << std::endl; } return input;}
void PhoneBook::addContact() { Contact newContact;
newContact.setFirstName(_getInput("Enter first name: ")); newContact.setLastName(_getInput("Enter last name: ")); newContact.setNickname(_getInput("Enter nickname: ")); newContact.setPhoneNumber(_getInput("Enter phone number: ")); newContact.setDarkestSecret(_getInput("Enter darkest secret: ")); newContact.setIsEmpty(false);
_contacts[_currentIndex] = newContact; _currentIndex = (_currentIndex + 1) % 8; if (_totalContacts < 8) _totalContacts++;
std::cout << "Contact added successfully!" << std::endl;}
void PhoneBook::searchContacts() const { if (_totalContacts == 0) { std::cout << "PhoneBook is empty." << std::endl; return; }
// Display header std::cout << std::setw(10) << "Index" << "|"; std::cout << std::setw(10) << "First Name" << "|"; std::cout << std::setw(10) << "Last Name" << "|"; std::cout << std::setw(10) << "Nickname" << std::endl; std::cout << std::string(44, '-') << std::endl;
// Display all contacts for (int i = 0; i < _totalContacts; i++) _displayContactRow(i);
// Get index from user std::cout << "Enter index to view details: "; std::string indexStr; if (!std::getline(std::cin, indexStr)) { std::cout << std::endl; return; }
// Validate index if (indexStr.length() != 1 || indexStr[0] < '0' || indexStr[0] > '7') { std::cout << "Invalid index." << std::endl; return; }
int index = indexStr[0] - '0'; if (index >= _totalContacts) { std::cout << "Invalid index." << std::endl; return; }
// Display full contact details std::cout << "First Name: " << _contacts[index].getFirstName() << std::endl; std::cout << "Last Name: " << _contacts[index].getLastName() << std::endl; std::cout << "Nickname: " << _contacts[index].getNickname() << std::endl; std::cout << "Phone Number: " << _contacts[index].getPhoneNumber() << std::endl; std::cout << "Darkest Secret: " << _contacts[index].getDarkestSecret() << std::endl;}main.cpp
#include "PhoneBook.hpp"#include <iostream>#include <cstdlib>
int main() { PhoneBook phonebook; std::string command;
while (true) { std::cout << "Enter command (ADD, SEARCH, EXIT): "; if (!std::getline(std::cin, command)) { std::cout << std::endl; break; }
if (command == "ADD") phonebook.addContact(); else if (command == "SEARCH") phonebook.searchContacts(); else if (command == "EXIT") break; }
return 0;}Exercise 02: The Job Of Your Dreams (Bonus)
Section titled “Exercise 02: The Job Of Your Dreams (Bonus)”Note: This is a BONUS exercise. Complete it after ex00 and ex01 if you want extra practice with static members and reverse-engineering.
This exercise is different. Instead of building from scratch, you’re given pieces and must deduce the missing parts. You receive:
Account.hpp- The complete class declarationtests.cpp- Test code that uses the class- A log file - The expected output showing what should happen
Your job: Write Account.cpp to make the tests produce output matching the log (except timestamps).
Subject Analysis
Section titled “Subject Analysis”What you’re given:
The header file Account.hpp declares:
- Static members tracking global state (
_nbAccounts,_totalAmount, etc.) - Instance members for each account (
_accountIndex,_amount, etc.) - A private
_displayTimestamp()method - Constructor, destructor, and various methods
What you must figure out:
- How to initialize static members (they MUST be defined somewhere)
- How to format the timestamp from the log:
[YYYYMMDD_HHMMSS] - The exact output format (semicolon-separated
key:valuepairs) - What each method should print and when
Approach Strategy
Section titled “Approach Strategy”Step 1 - Analyze the header file
Read every line of Account.hpp. List what you need to implement:
- 4 static members need initialization
- 4 static getter methods
- 1 static display method (
displayAccountsInfos) - Constructor and destructor
makeDeposit,makeWithdrawal,checkAmount,displayStatus- Private
_displayTimestamp
Step 2 - Study the log file
The log shows EXACTLY what output to produce. Key observations:
- Every line starts with a timestamp:
[19920104_091532] - Format is semicolon-separated:
index:0;amount:42;created - Destructor calls happen in REVERSE order of construction
Step 3 - Map log lines to methods
| Log Pattern | Method |
|---|---|
accounts:N;total:N;deposits:N;withdrawals:N | displayAccountsInfos() |
index:N;amount:N;created | Constructor |
index:N;amount:N;closed | Destructor |
index:N;p_amount:N;deposit:N;amount:N;nb_deposits:N | makeDeposit() |
index:N;p_amount:N;withdrawal:refused | makeWithdrawal() (fail) |
index:N;p_amount:N;withdrawal:N;amount:N;nb_withdrawals:N | makeWithdrawal() (success) |
Progressive Code Building
Section titled “Progressive Code Building”Stage 1 - Static member initialization:
#include "Account.hpp"
// CRITICAL: Static members MUST be initialized outside the class// This allocates actual storage for themint Account::_nbAccounts = 0;int Account::_totalAmount = 0;int Account::_totalNbDeposits = 0;int Account::_totalNbWithdrawals = 0;Why outside the class? Static members are shared by ALL instances. The header only declares them; you must define (allocate storage for) them exactly once in a .cpp file.
Stage 2 - Timestamp formatting:
#include <ctime>#include <iomanip>
void Account::_displayTimestamp() { std::time_t now = std::time(NULL); std::tm* local = std::localtime(&now);
std::cout << "[" << (local->tm_year + 1900) // Year since 1900 << std::setfill('0') << std::setw(2) << (local->tm_mon + 1) // Month 0-11 << std::setfill('0') << std::setw(2) << local->tm_mday << "_" << std::setfill('0') << std::setw(2) << local->tm_hour << std::setfill('0') << std::setw(2) << local->tm_min << std::setfill('0') << std::setw(2) << local->tm_sec << "] ";}Understanding <ctime>:
| Function | Purpose |
|---|---|
std::time(NULL) | Get current time as seconds since epoch |
std::localtime(&now) | Convert to local time struct (tm) |
tm->tm_year | Years since 1900 (add 1900 for actual year) |
tm->tm_mon | Month 0-11 (add 1 for human-readable) |
Stage 3 - Constructor and destructor:
Account::Account(int initial_deposit) : _accountIndex(_nbAccounts), // Capture current count BEFORE increment _amount(initial_deposit), _nbDeposits(0), _nbWithdrawals(0) { _nbAccounts++; // NOW increment for next account _totalAmount += initial_deposit;
_displayTimestamp(); std::cout << "index:" << _accountIndex << ";amount:" << _amount << ";created" << std::endl;}
Account::~Account() { _displayTimestamp(); std::cout << "index:" << _accountIndex << ";amount:" << _amount << ";closed" << std::endl;}Why does destructor order matter?
C++ destroys objects in REVERSE order of construction. For arrays:
- First created (index 0) → last destroyed
- Last created (index 7) → first destroyed
The log file shows this clearly - check that your destructor output matches.
Common Pitfalls
Section titled “Common Pitfalls”1. Initializing static members in the header
// WRONG - in Account.hppclass Account {private: static int _nbAccounts = 0; // ERROR: can't initialize here (pre-C++17)};
// RIGHT - in Account.cppint Account::_nbAccounts = 0; // Definition with initialization2. Forgetting to add 1900/1 to time values
// WRONGstd::cout << local->tm_year; // Prints "125" for year 2025!std::cout << local->tm_mon; // Prints "0" for January!
// RIGHTstd::cout << (local->tm_year + 1900); // Prints "2025"std::cout << (local->tm_mon + 1); // Prints "1"3. Wrong output format
// WRONG - spaces and different separatorsstd::cout << "index: " << _accountIndex << ", amount: " << _amount;
// RIGHT - match the log exactlystd::cout << "index:" << _accountIndex << ";amount:" << _amount;4. Not understanding destructor order
When you create accounts 0, 1, 2… they’re destroyed 2, 1, 0. The log file confirms this - verify your output matches.
Testing Tips
Section titled “Testing Tips”Since you have a reference log file, testing is straightforward:
# Compile your implementationc++ -Wall -Wextra -Werror Account.cpp tests.cpp -o account_test
# Run and capture output./account_test > my_output.txt
# Compare with reference (ignoring timestamps)# The timestamps will differ, but everything else should match
# Strip timestamps for comparisonsed 's/\[[0-9_]*\]/[TIMESTAMP]/g' my_output.txt > my_clean.txtsed 's/\[[0-9_]*\]/[TIMESTAMP]/g' reference.log > ref_clean.txtdiff my_clean.txt ref_clean.txtWhat to verify:
- Account creation messages appear in order (0, 1, 2…)
- Account destruction messages appear in REVERSE order
- Withdrawal refusals say exactly “refused”
- All semicolons and colons are in the right places
Final Code
Section titled “Final Code”#include "Account.hpp"#include <iostream>#include <iomanip>#include <ctime>
// Initialize static membersint Account::_nbAccounts = 0;int Account::_totalAmount = 0;int Account::_totalNbDeposits = 0;int Account::_totalNbWithdrawals = 0;
// Static gettersint Account::getNbAccounts() { return _nbAccounts; }int Account::getTotalAmount() { return _totalAmount; }int Account::getNbDeposits() { return _totalNbDeposits; }int Account::getNbWithdrawals() { return _totalNbWithdrawals; }
// Display timestamp: [YYYYMMDD_HHMMSS]void Account::_displayTimestamp() { std::time_t now = std::time(NULL); std::tm* local = std::localtime(&now);
std::cout << "[" << (local->tm_year + 1900) << std::setfill('0') << std::setw(2) << (local->tm_mon + 1) << std::setfill('0') << std::setw(2) << local->tm_mday << "_" << std::setfill('0') << std::setw(2) << local->tm_hour << std::setfill('0') << std::setw(2) << local->tm_min << std::setfill('0') << std::setw(2) << local->tm_sec << "] ";}
// Display all accounts infovoid Account::displayAccountsInfos() { _displayTimestamp(); std::cout << "accounts:" << _nbAccounts << ";total:" << _totalAmount << ";deposits:" << _totalNbDeposits << ";withdrawals:" << _totalNbWithdrawals << std::endl;}
// ConstructorAccount::Account(int initial_deposit) : _accountIndex(_nbAccounts), _amount(initial_deposit), _nbDeposits(0), _nbWithdrawals(0) { _nbAccounts++; _totalAmount += initial_deposit;
_displayTimestamp(); std::cout << "index:" << _accountIndex << ";amount:" << _amount << ";created" << std::endl;}
// DestructorAccount::~Account() { _displayTimestamp(); std::cout << "index:" << _accountIndex << ";amount:" << _amount << ";closed" << std::endl;}
// Make a depositvoid Account::makeDeposit(int deposit) { _displayTimestamp(); std::cout << "index:" << _accountIndex << ";p_amount:" << _amount << ";deposit:" << deposit;
_amount += deposit; _nbDeposits++; _totalAmount += deposit; _totalNbDeposits++;
std::cout << ";amount:" << _amount << ";nb_deposits:" << _nbDeposits << std::endl;}
// Make a withdrawalbool Account::makeWithdrawal(int withdrawal) { _displayTimestamp(); std::cout << "index:" << _accountIndex << ";p_amount:" << _amount << ";withdrawal:";
if (withdrawal > _amount) { std::cout << "refused" << std::endl; return false; }
_amount -= withdrawal; _nbWithdrawals++; _totalAmount -= withdrawal; _totalNbWithdrawals++;
std::cout << withdrawal << ";amount:" << _amount << ";nb_withdrawals:" << _nbWithdrawals << std::endl; return true;}
// Check amountint Account::checkAmount() const { return _amount;}
// Display statusvoid Account::displayStatus() const { _displayTimestamp(); std::cout << "index:" << _accountIndex << ";amount:" << _amount << ";deposits:" << _nbDeposits << ";withdrawals:" << _nbWithdrawals << std::endl;}Quick Reference: C to C++ Translation
Section titled “Quick Reference: C to C++ Translation”| Task | C | C++ |
|---|---|---|
printf("x=%d\n", x); | std::cout << "x=" << x << std::endl; | |
| Read int | scanf("%d", &x); | std::cin >> x; |
| Read line | fgets(buf, size, stdin); | std::getline(std::cin, str); |
| Allocate | malloc(n * sizeof(int)) | new int[n] |
| Free | free(ptr) | delete[] ptr |
| String | char str[100] | std::string str |
| String length | strlen(str) | str.length() |
| String copy | strcpy(dst, src) | dst = src |
| String compare | strcmp(a, b) == 0 | a == b |
Related Concepts
Section titled “Related Concepts”Now that you understand C++ fundamentals, continue your learning:
- Next: Module 01: Memory & References - Learn about stack vs heap, references vs pointers, and file I/O
- Later: Module 02: OCF & Operators - Learn the Orthodox Canonical Form pattern required for all your classes
Key Terms from This Module
Section titled “Key Terms from This Module”Visit the Glossary for definitions of: