Skip to content

Module 00: C++ Fundamentals

Download Official Subject PDF

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

Before diving into syntax, understand WHY these concepts exist:

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.

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.

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:

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::endl inserts a newline and flushes the buffer
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
std::cout // Access cout from the std namespace
ClassName::method // Access method from ClassName
  • :: is the “scope resolution operator”
  • It tells the compiler WHERE to look for a name (variable, function, class)
  • std::cout means “use the cout that belongs to the std namespace”
  • This prevents naming conflicts when different libraries use the same names
object.method() // Access method of an object
object.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)
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
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)
*ptr = 42; // Store 42 at the address ptr points to
int 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

CC++
printf()std::cout <<
scanf()std::cin >>
malloc()/free()new/delete
struct (data only)class (data + behavior)
Functions operate on dataObjects have methods
// FORBIDDEN - will get you 0
printf("Hello"); // Forbidden: *printf(), use std::cout instead
malloc(sizeof(int)); // Forbidden: *alloc() (malloc/calloc/realloc), use new instead
free(ptr); // Forbidden, use delete instead
// FORBIDDEN - will get you -42
using 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 08

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 A
void print(); // Library B - ERROR!
// With namespaces - no collision
namespace LibraryA {
void print();
}
namespace LibraryB {
void print();
}
// Usage
LibraryA::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.

Everything from the C++ standard library lives in std:

std::cout // output stream
std::cin // input stream
std::cerr // error stream
std::string // string class
std::endl // newline + flush
std::cout // cout from std namespace
::globalFunction() // function from global namespace (no namespace)
ClassName::method // method from ClassName

#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;
}
#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;
}
// PROBLEM: mixing cin >> and getline
int age;
std::string name;
std::cin >> age; // Leaves '\n' in buffer
std::getline(std::cin, name); // Reads empty line!
// SOLUTION: clear the buffer
std::cin >> age;
std::cin.ignore(); // Ignore the leftover '\n'
std::getline(std::cin, name);
#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;
}

// 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 automatically
#include <string>
std::string s = "Hello";
// Length
s.length(); // 5
s.size(); // 5 (same thing)
// Access characters
s[0]; // 'H'
s.at(0); // 'H' (with bounds checking)
// Concatenation
s + " World"; // "Hello World"
s.append(" World"); // Modifies s
// Comparison
s == "Hello"; // true
s < "World"; // true (lexicographic)
// Substrings
s.substr(0, 3); // "Hel"
s.substr(2); // "llo"
// Find
s.find("ll"); // 2 (index)
s.find("xyz"); // std::string::npos (not found)
// Replace (but remember: forbidden in ex04!)
s.replace(0, 2, "YY"); // "YYllo"
// Clear
s.empty(); // false
s.clear(); // s is now ""
std::string s = "Hello";
// Index-based
for (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) { } // FORBIDDEN

In C++, both class and struct can have member variables and functions. The only difference is the default access specifier:

Featureclassstruct
Default accessprivatepublic
Default inheritanceprivatepublic
Members accessible?Only inside classFrom 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 public

When to use each:

  • Use class when you need encapsulation (hide implementation)
  • Use struct for 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)
Contact.hpp
#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
Contact.cpp
#include "Contact.hpp"
#include <iostream>
// Constructor implementation
Contact::Contact() {
std::cout << "Contact created" << std::endl;
}
// Destructor implementation
Contact::~Contact() {
std::cout << "Contact destroyed" << std::endl;
}
// Setter
void Contact::setFirstName(std::string name) {
this->_firstName = name;
}
// Getter (const - doesn't modify object)
std::string Contact::getFirstName() const {
return this->_firstName;
}
// Display method
void Contact::display() const {
std::cout << "Name: " << _firstName << " " << _lastName << std::endl;
}
SpecifierAccess
privateOnly accessible within the class
publicAccessible from anywhere
protectedAccessible in class and derived classes

Rule of thumb: Make attributes private, provide public getters/setters.

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.

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; }
};

class MyClass {
public:
MyClass() {
std::cout << "Default constructor called" << std::endl;
}
};
// Usage
MyClass obj; // Calls default constructor
class Contact {
private:
std::string _name;
int _age;
public:
Contact(std::string name, int age) {
_name = name;
_age = age;
}
};
// Usage
Contact c("John", 25);
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?

  1. 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'
    }
  2. Required for const members (can’t assign to const after construction)

  3. Required for reference members (must be bound at initialization)

  4. 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) {}
};
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;
}
};

Shared across ALL instances of a class.

// Header
class 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
// Usage
Counter a;
Counter b;
Counter c;
std::cout << Counter::getCount(); // 3

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 needed
int result = Math::add(5, 3);

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 int MAX = 100; // Cannot be modified
MAX = 200; // ERROR!
void print(const std::string& s) {
// s cannot be modified
// Passed by reference (efficient, no copy)
std::cout << s << std::endl;
}
class Example {
public:
int getValue() const { // Promises not to modify object
return _value;
}
};
class Example {
private:
std::string _name;
public:
// Returns const reference - caller cannot modify
const std::string& getName() const {
return _name;
}
};

Every header file MUST have include guards to prevent double inclusion:

Contact.hpp
#ifndef CONTACT_HPP
#define CONTACT_HPP
class Contact {
// ...
};
#endif

Why?

// 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, skipped

NAME = program
CXX = c++
CXXFLAGS = -Wall -Wextra -Werror -std=c++98
SRCS = main.cpp Contact.cpp PhoneBook.cpp
OBJS = $(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 re

This structure also avoids “relinking”: if none of the .cpp files changed, make will not re-run the final link step.

LineCodeExplanation
1NAME = programDefines a variable NAME containing the final executable name. Using a variable makes it easy to change the program name in one place.
2CXX = 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.
3CXXFLAGS = -Wall -Wextra -Werror -std=c++98Compiler 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).
5SRCS = main.cpp Contact.cpp PhoneBook.cppLists all source files to compile. Each .cpp file will be compiled into its own object file.
6OBJS = $(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.
8all: $(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: %.cppPattern 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-17clean:A target to remove object files. Has no prerequisites, so it always runs when called.
17rm -f $(OBJS)Removes all object files. -f means “force” (don’t error if files don’t exist).
19-20fclean: clean”Full clean”: first runs clean (its prerequisite), then removes the executable too.
20rm -f $(NAME)Removes the final executable.
22re: fclean all”Remake”: runs fclean first, then all. This forces a complete recompilation from scratch.
24.PHONY: all clean fclean reDeclares these targets as “phony” (not actual files). Without this, if a file named clean existed, make clean wouldn’t run the rule.
VariableMeaning
$@The target of the rule (what we’re building)
$<The first prerequisite (first dependency)
$^All prerequisites (all dependencies)
  1. Incremental compilation: Only recompiles files that changed
  2. No relinking: If no object files changed, the executable isn’t rebuilt
  3. Easy maintenance: Change NAME or SRCS in one place
  4. Clean targets: clean, fclean, and re follow 42’s standard conventions

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 EOF
while (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;
}
#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
}

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++;
}
};
int index = 0;
int size = 8;
index = (index + 1) % size; // 0 -> 1
index = (index + 1) % size; // 1 -> 2
// ... after 7:
index = (index + 1) % size; // 7 -> 0 (wraps around!)

C++ extends the pointer concept to allow pointers to class members (both attributes and member functions). This is different from regular pointers.

class Sample {
public:
int value;
};
// Pointer to member attribute
int Sample::*ptr = &Sample::value;
// Usage - need an instance to dereference
Sample s;
s.value = 42;
std::cout << s.*ptr << std::endl; // Prints 42
Sample* sp = &s;
std::cout << sp->*ptr << std::endl; // Also prints 42
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;
// Usage
Sample s;
(s.*funcPtr)(); // Calls s.display()
Sample* sp = &s;
(sp->*funcPtr)(); // Also calls display()
// Pointer to member function with parameters
void (Sample::*greetPtr)(std::string) = &Sample::greet;
(s.*greetPtr)("World"); // Calls s.greet("World")
  • 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

  1. Using using namespace std; - Forbidden at 42 (grade -42)
  2. Using friend keyword - Forbidden unless explicitly allowed (grade -42)
  3. Using STL containers/algorithms before Module 08 - Forbidden (grade -42)
  4. Forgetting include guards - Causes compilation errors (grade 0)
  5. Putting implementation in headers - Grade 0 (except templates)
  6. Not ending output with newline - Required by subject
  7. Using printf/scanf - Forbidden at 42 (grade 0)
  8. Forgetting const on getters - Bad practice
  9. Not initializing members - Undefined behavior
  10. Memory leaks - Always pair new with delete

Now let’s apply these concepts to the module exercises.


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++.

Before writing any code, think through the problem:

  1. Understand argc/argv: argc is the argument count, argv is an array of C-strings. argv[0] is always the program name.
  2. Handle the edge case first: Check if no arguments were given (argc == 1).
  3. Process each argument: Loop from index 1 to skip the program name.
  4. Convert character by character: For each argument, loop through each character and convert to uppercase.
  5. Output at the end: Print a single newline after all characters.

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;
}
LineCodeWhy
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().
4int main(int argc, char **argv)Standard signature for programs receiving command-line arguments.
6if (argc == 1)Why 1, not 0? Because argv[0] is always the program name, so 1 means “no actual arguments”.
8std::cout << ... << std::endl;Stream the message to stdout. std::endl flushes the buffer and adds newline.
9return 0;Exit early after printing the default message.
12for (int i = 1; i < argc; i++)Start at 1 to skip argv[0] (the program name).
14for (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.
19std::cout << std::endl;Print final newline only after all characters are output.
  1. Using printf instead of std::cout

    • C habit! In C++ modules, you must use std::cout for output.
    • Wrong: printf("hello");
    • Right: std::cout << "hello";
  2. 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);
  3. 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).
  4. Adding spaces between arguments

    • The subject wants arguments concatenated directly.
    • ./megaphone "Hello" "World" should output HELLOWORLD, not HELLO WORLD.

Test your program with the exact examples from the subject:

Terminal window
./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:

Terminal window
./megaphone ""
# Expected: (empty line - just a newline)
./megaphone "123" "!@#"
# Expected: 123!@# (non-letters pass through unchanged)
megaphone.cpp
#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;
}

Your first real C++ program with classes. This exercise teaches you object-oriented fundamentals: encapsulation, class design, and how objects interact.

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:

  1. Display a formatted table showing all contacts
  2. Prompt for an index, then display that contact’s full details

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::getline returns 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.

Stage 1 - Contact Class (the data container):

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();
// 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;
};
#endif

Why these design decisions?

DecisionReason
Private attributesEncapsulation - external code can’t corrupt data
const std::string& paramsAvoids copying the string, more efficient
const on gettersPromises these methods won’t modify the object
_isEmpty flagLets us check if a contact slot is actually used
Underscore prefixCommon convention to identify private members

Stage 2 - PhoneBook Class (the manager):

PhoneBook.hpp
#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;
};
#endif

Why 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% 8Result
011 % 81
677 % 87
788 % 80 ← 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 output
  • std::right - Align content to the right within that width

Example: "John" becomes " John" (6 spaces + 4 chars = 10)

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;
}
LineWhy
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 index
std::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-7
if (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 int
if (index >= _totalContacts) {
std::cout << "Invalid index." << std::endl;
return;
}
LineWhy
indexStr.length() != 1Only 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 >= _totalContactsCan’t access contact that doesn’t exist

1. Using vectors or dynamic allocation

The subject EXPLICITLY forbids this. You will fail the evaluation.

// WRONG - forbidden
std::vector<Contact> _contacts;
Contact* _contacts = new Contact[8];
// RIGHT - fixed array
Contact _contacts[8];

2. Wrong display formatting

// WRONG - no formatting
std::cout << firstName << "|" << lastName << std::endl;
// RIGHT - 10 chars, right-aligned, truncated
std::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 anything
std::getline(std::cin, input);
contact.setFirstName(input);
// RIGHT - loop until non-empty
while (input.empty()) {
std::cout << "Field cannot be empty: ";
std::getline(std::cin, input);
}

5. Not handling EOF (Ctrl+D)

// WRONG - ignores EOF, program hangs
std::getline(std::cin, input);
// RIGHT - check return value and exit
if (!std::getline(std::cin, input)) {
std::cout << std::endl;
std::exit(0);
}

6. Making attributes public

// WRONG - breaks encapsulation
class Contact {
public:
std::string firstName; // Anyone can modify directly!
};
// RIGHT - private with accessors
class Contact {
private:
std::string _firstName;
public:
void setFirstName(const std::string& name);
std::string getFirstName() const;
};
Terminal window
# Test ADD functionality
> ADD
Enter first name: John
Enter last name: Doe
Enter nickname: JD
Enter phone number: 555-1234
Enter darkest secret: Likes pineapple pizza
Contact added successfully!
# Test empty field rejection
> ADD
Enter first name:
Field cannot be empty. Please try again.
Enter first name: Jane
# Test SEARCH with no contacts
> SEARCH
PhoneBook is empty.
# Test SEARCH display formatting
> SEARCH
Index|First Name| Last Name| Nickname
--------------------------------------------
0| John| Doe| JD
Enter index to view details: 0
First Name: John
Last Name: Doe
Nickname: JD
Phone Number: 555-1234
Darkest 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
> SEARCH
Enter index to view details: 9
Invalid index.
> SEARCH
Enter index to view details: abc
Invalid index.
# Test EOF handling
> (Press Ctrl+D)
# Program should exit cleanly with newline

Contact.hpp

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;
};
#endif

Contact.cpp

Contact.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

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;
};
#endif

PhoneBook.cpp

PhoneBook.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

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 declaration
  • tests.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).

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:

  1. How to initialize static members (they MUST be defined somewhere)
  2. How to format the timestamp from the log: [YYYYMMDD_HHMMSS]
  3. The exact output format (semicolon-separated key:value pairs)
  4. What each method should print and when

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 PatternMethod
accounts:N;total:N;deposits:N;withdrawals:NdisplayAccountsInfos()
index:N;amount:N;createdConstructor
index:N;amount:N;closedDestructor
index:N;p_amount:N;deposit:N;amount:N;nb_deposits:NmakeDeposit()
index:N;p_amount:N;withdrawal:refusedmakeWithdrawal() (fail)
index:N;p_amount:N;withdrawal:N;amount:N;nb_withdrawals:NmakeWithdrawal() (success)

Stage 1 - Static member initialization:

#include "Account.hpp"
// CRITICAL: Static members MUST be initialized outside the class
// This allocates actual storage for them
int 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>:

FunctionPurpose
std::time(NULL)Get current time as seconds since epoch
std::localtime(&now)Convert to local time struct (tm)
tm->tm_yearYears since 1900 (add 1900 for actual year)
tm->tm_monMonth 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.

1. Initializing static members in the header

// WRONG - in Account.hpp
class Account {
private:
static int _nbAccounts = 0; // ERROR: can't initialize here (pre-C++17)
};
// RIGHT - in Account.cpp
int Account::_nbAccounts = 0; // Definition with initialization

2. Forgetting to add 1900/1 to time values

// WRONG
std::cout << local->tm_year; // Prints "125" for year 2025!
std::cout << local->tm_mon; // Prints "0" for January!
// RIGHT
std::cout << (local->tm_year + 1900); // Prints "2025"
std::cout << (local->tm_mon + 1); // Prints "1"

3. Wrong output format

// WRONG - spaces and different separators
std::cout << "index: " << _accountIndex << ", amount: " << _amount;
// RIGHT - match the log exactly
std::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.

Since you have a reference log file, testing is straightforward:

Terminal window
# Compile your implementation
c++ -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 comparison
sed 's/\[[0-9_]*\]/[TIMESTAMP]/g' my_output.txt > my_clean.txt
sed 's/\[[0-9_]*\]/[TIMESTAMP]/g' reference.log > ref_clean.txt
diff my_clean.txt ref_clean.txt

What to verify:

  1. Account creation messages appear in order (0, 1, 2…)
  2. Account destruction messages appear in REVERSE order
  3. Withdrawal refusals say exactly “refused”
  4. All semicolons and colons are in the right places
Account.cpp
#include "Account.hpp"
#include <iostream>
#include <iomanip>
#include <ctime>
// Initialize static members
int Account::_nbAccounts = 0;
int Account::_totalAmount = 0;
int Account::_totalNbDeposits = 0;
int Account::_totalNbWithdrawals = 0;
// Static getters
int 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 info
void Account::displayAccountsInfos() {
_displayTimestamp();
std::cout << "accounts:" << _nbAccounts
<< ";total:" << _totalAmount
<< ";deposits:" << _totalNbDeposits
<< ";withdrawals:" << _totalNbWithdrawals << std::endl;
}
// Constructor
Account::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;
}
// Destructor
Account::~Account() {
_displayTimestamp();
std::cout << "index:" << _accountIndex
<< ";amount:" << _amount
<< ";closed" << std::endl;
}
// Make a deposit
void 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 withdrawal
bool 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 amount
int Account::checkAmount() const {
return _amount;
}
// Display status
void Account::displayStatus() const {
_displayTimestamp();
std::cout << "index:" << _accountIndex
<< ";amount:" << _amount
<< ";deposits:" << _nbDeposits
<< ";withdrawals:" << _nbWithdrawals << std::endl;
}

TaskCC++
Printprintf("x=%d\n", x);std::cout << "x=" << x << std::endl;
Read intscanf("%d", &x);std::cin >> x;
Read linefgets(buf, size, stdin);std::getline(std::cin, str);
Allocatemalloc(n * sizeof(int))new int[n]
Freefree(ptr)delete[] ptr
Stringchar str[100]std::string str
String lengthstrlen(str)str.length()
String copystrcpy(dst, src)dst = src
String comparestrcmp(a, b) == 0a == b

Now that you understand C++ fundamentals, continue your learning:

Visit the Glossary for definitions of: