JB logo
CoffeeyOUTUBE
Back to Learning C++

C++ · Reference notes

C++ Complete Notes

Compiled from beginner C++ video courses (Bro Code style + Dave/Mike style tutorials)


Table of Contents

  1. Getting Started
  2. Your First Program
  3. Variables & Data Types
  4. Constants
  5. Namespaces
  6. Type Aliases (typedef / using)
  7. Operators
  8. Type Conversion
  9. User Input
  10. Math Functions
  11. Strings
  12. Control Flow: if / else
  13. Switch Statements
  14. Ternary Operator
  15. Logical Operators
  16. Loops
  17. Break & Continue
  18. Arrays
  19. Multidimensional Arrays
  20. Functions
  21. Return Keyword
  22. Overloaded Functions
  23. Variable Scope
  24. Random Numbers
  25. Memory Addresses & Pointers
  26. Pass by Value vs Pass by Reference
  27. Const Parameters
  28. Dynamic Memory
  29. Recursion
  30. Function Templates
  31. Structs
  32. Enums
  33. Object-Oriented Programming
  34. Constructors
  35. Object (Member) Functions
  36. Getters & Setters
  37. Inheritance
  38. Mini Practice Projects

Getting Started

C++ is a fast, middle-level language — closely related to C. It's used in graphics-intensive applications, video editing software, embedded systems, and video games. It sits between low-level languages (close to hardware) and high-level languages (like Python/Java, easier to read but slower).

What You Need

  1. A text editor / IDE — e.g. VS Code, Code::Blocks, Notepad++. Code::Blocks bundles the compiler too, which is convenient for beginners.
  2. A compiler — translates C++ source code into machine instructions.
    • Windows/Linux → GCC (MinGW on Windows)
    • Mac → Clang (or run xcode-select --install in Terminal, then check with gcc -v)

Basic Workflow

  • Build = convert your code into machine-understandable instructions.
  • Run = execute those instructions.
  • File extension: .cpp
#include <iostream>
using namespace std;
 
int main() {
    cout << "Hello World" << endl;
    return 0;
}
  • #include <iostream> — imports the input/output library.
  • using namespace std; — lets you skip typing std:: before things like cout.
  • int main() { ... } — the entry point of every C++ program; code inside runs top to bottom.
  • return 0; — signals the program ended without errors (non-zero = a problem occurred).

Your First Program

#include <iostream>
 
int main() {
    std::cout << "I like pizza" << std::endl;   // end line, flushes buffer
    std::cout << "It's really good" << "\n";     // '\n' = new line char (faster, no flush)
    // This is a single-line comment
    /* This is a
       multi-line comment */
    return 0;
}
  • std::cout <<character output ("standard character output"). << is the insertion/left-shift operator.
  • endl vs \n — both create new lines; endl also flushes the output buffer (slightly slower), \n is a plain newline character.
  • Every statement ends with a semicolon ;.

Variables & Data Types

A variable is a named container for a value stored in memory. Two steps: declaration (naming + typing it) and assignment (giving it a value). You can do both at once.

int x;          // declaration
x = 5;          // assignment
int y = 6;      // declaration + assignment in one line

Common Primitive Types

TypeStoresExample
intWhole numberint age = 21;
doubleNumber with decimal (more precision)double price = 10.99;
floatNumber with decimal (less precision)float temp = 98.6f;
charA single character (single quotes)char grade = 'A';
booltrue or falsebool isStudent = true;
std::stringA sequence of text (double quotes)string name = "Bro Code";

Notes:

  • If you assign a decimal to an int, the decimal part is truncated (not rounded): int days = 7.5;days becomes 7.
  • char can only hold ONE character — assigning more causes overflow and only the last character is kept.
  • double allows more decimal precision than float.
  • You can print text + variables together:
cout << "Hello " << name << endl;
cout << "You are " << age << " years old" << endl;
  • A value typed directly (not stored in a variable) is called a constant/literal, e.g. cout << 4.5;.

Constants

The const keyword makes a variable read-only — the compiler prevents any modification after initialization.

const double PI = 3.14159;         // convention: ALL_CAPS for constants
double radius = 10;
double circumference = 2 * PI * radius;

Trying to reassign PI later causes a compile error ("assignment of read-only variable"). Use const any time a value should never change (physical constants, screen dimensions, etc.).


Namespaces

A namespace prevents naming conflicts by grouping identically-named entities separately.

namespace first  { int x = 1; }
namespace second { int x = 2; }
 
int x = 0;               // global/local x
 
cout << x;                       // 0 (local version)
cout << first::x;                // 1 (scope resolution operator ::)
cout << second::x;               // 2
 
using namespace first;   // now "x" without prefix refers to first::x
  • using namespace std; avoids typing std:: everywhere, but with large programs it increases the chance of naming collisions.
  • Safer alternative: using std::cout; (only import what you need).

Type Aliases

typedef (older) and using (modern, preferred — works better with templates) create a nickname for an existing data type.

typedef std::string text_t;
text_t firstName = "Bro";
 
using number_t = int;
number_t age = 21;

Common naming convention: alias name ends in _t. Use aliases only when there's a clear readability benefit (e.g. shortening a long templated type like std::vector<std::pair<std::string,int>>).


Operators

Arithmetic Operators

OperatorMeaningShorthand
+Additionx += 1;
-Subtractionx -= 1;
*Multiplicationx *= 2;
/Divisionx /= 2;
%Modulus (remainder)
++Increment by 1x++;
--Decrement by 1x--;
int students = 20;
students++;              // 21 (preferred way to add 1)
int remainder = students % 3;  // remainder of division — great for checking even/odd
  • Integer division between two ints discards the decimal (10 / 33). Cast at least one operand to double to keep the decimal: 10 / 3.03.333....
  • Order of operations follows standard math rules: parentheses → multiplication/division → addition/subtraction. Use () to force a different order.

Comparison Operators

== (equal), != (not equal), >, <, >=, <= — each evaluates to a bool (true/false).


Type Conversion

Implicit conversion happens automatically (e.g. assigning 3.14 to an int truncates it to 3). Explicit conversion (casting): put the target type in parentheses before the value.

int x = (int)3.14;              // explicit cast → 3
double score = (double)correct / questions * 100;  // avoids integer-division bug

User Input

string name;
cout << "What's your name? ";
cin >> name;                     // stops reading at first whitespace!
 
int age;
cout << "What's your age? ";
cin >> age;
 
// For strings that may contain spaces, use getline:
string fullName;
cout << "What's your full name? ";
cin >> ws;                       // eats leftover newline/whitespace in the buffer
getline(cin, fullName);
  • cin >> (extraction operator, two right angle brackets) reads a single token (int, char, or word — no spaces).
  • getline(cin, variable) reads an entire line, including spaces.
  • Mixing cin >> then getline can cause the getline to grab a leftover newline — fix with cin >> ws before the getline.
  • cin.clear(); and cin.ignore() / cin.sync() help reset the input buffer after invalid input (e.g. clear error flags then flush the stream).

Math Functions

Requires #include <cmath>.

std::max(x, y);      std::min(x, y);
pow(base, exponent);
sqrt(x);
abs(x);
round(x);   ceil(x);   floor(x);
  • round() rounds normally, ceil() always rounds up, floor() always rounds down.
  • srand(time(0)); seeds the pseudo-random number generator (needs #include <ctime>); rand() then generates a number 0 to RAND_MAX.
  • To get a random number in a range: rand() % range + minValue (e.g. rand() % 6 + 1 for a 6-sided die).

Strings

#include <string> (usually already available via <iostream> + using namespace std;).

MethodEffect
str.length()number of characters
str.empty()true if string has no characters
str.clear()erases all contents
str.append("text")adds text to the end
str[i]access character at index i (0-based)
str.insert(i, "text")inserts text at position i
str.find("x")index of first occurrence (or string::npos if not found)
str.erase(start, count)removes count characters starting at start
str.substr(start, len)extracts a substring
string phrase = "draft Academy";
cout << phrase.length();          // 15 (index starts at 0)
cout << phrase[0];                // 'd'
phrase[0] = 'b';                  // modify a single character
cout << phrase.find("Academy");   // 8
cout << phrase.substr(8, 3);      // "Aca"

Concatenating a string with a variable inline:

cout << "Hello " << name << ", you are " << age << " years old" << endl;

Control Flow: if / else

int age;
cin >> age;
 
if (age >= 18) {
    cout << "Welcome to the site" << endl;
} else if (age < 0) {
    cout << "You haven't been born yet" << endl;
} else if (age >= 100) {
    cout << "You are too old to enter this site" << endl;
} else {
    cout << "You are not old enough to enter" << endl;
}
  • Conditions are checked top to bottom; order matters (more specific conditions should come first).
  • Comparison inside if() resolves to a bool — you can also just write a bool variable directly: if (isStudent) { ... }.
  • Use ! (negation/NOT) to flip a condition: if (!isRaining) { ... }.

Switch Statements

An efficient alternative to a long chain of else if statements when comparing ONE value against MANY specific values.

switch (month) {
    case 1:
        cout << "January";
        break;
    case 2:
        cout << "February";
        break;
    default:
        cout << "Invalid month";
        break;
}
  • Always break; at the end of each case, or execution "falls through" to the next case.
  • default: runs when no case matches (like else).
  • Only works with integral types, char, and enum values — not string (unless you convert to an enum or compare char by char).

Ternary Operator

Shorthand replacement for a simple if/else: condition ? valueIfTrue : valueIfFalse

string result = (grade >= 60) ? "You pass" : "You fail";
cout << (number % 2 == 1 ? "odd" : "even");
cout << (isHungry ? "You are hungry" : "You are full");

Logical Operators

OperatorMeaningExample
&&AND — both conditions must be trueif (temp > 0 && temp < 30)
||OR — at least one condition must be trueif (temp <= 0 || temp >= 30)
!NOT — reverses true/falseif (!isSunny)

Loops

While Loop

Checks the condition before each iteration.

int index = 1;
while (index <= 5) {
    cout << index << endl;
    index++;
}

⚠️ Forgetting to update the loop variable creates an infinite loop.

Do-While Loop

Runs the body once first, then checks the condition — guarantees at least one execution.

int number;
do {
    cout << "Enter a positive number: ";
    cin >> number;
} while (number < 0);

For Loop

Best when you know how many times to loop. Structure: for (init; condition; update)

for (int i = 1; i <= 10; i++) {
    cout << i << " ";
}
  • Can count by any step: i += 2, or count down: for (int i = 10; i >= 0; i--).
  • For-each loop — simplified syntax for iterating a full collection (less flexible, but concise):
for (string student : students) {
    cout << student << endl;
}

Nested Loops

A loop inside another loop — useful for grids, 2D data, and multiplication tables.

for (int i = 1; i <= rows; i++) {
    for (int j = 1; j <= columns; j++) {
        cout << symbol;
    }
    cout << endl;
}

Break & Continue

  • break; — exits the loop (or switch) immediately.
  • continue; — skips the rest of the current iteration and moves to the next one.
for (int i = 1; i <= 20; i++) {
    if (i == 13) continue;  // skips printing 13, keeps going
    cout << i << " ";
}

Arrays

A container that holds multiple values of the same data type, accessed by index (starting at 0).

string cars[] = {"Corvette", "Mustang", "Camry"};
cout << cars[0];              // "Corvette"
cars[0] = "Camaro";           // reassign an element
 
int numbers[10];              // declare with a fixed size, assign values later
numbers[0] = 100;
  • Arrays are a static data structure — size is fixed once declared.
  • sizeof(array) / sizeof(array[0]) calculates the number of elements.
  • Iterate with a standard for loop or a for-each loop.
  • Passing an array to a function: only pass the array name (no brackets); the array decays into a pointer, so the function loses knowledge of its size — pass the size as a separate argument too.
double getTotal(double prices[], int size) {
    double total = 0;
    for (int i = 0; i < size; i++) total += prices[i];
    return total;
}
  • Linear search: loop through every element checking for a match; return the index, or -1 (a common "not found" sentinel value) if nothing matches.
  • Bubble sort: repeatedly compare adjacent elements and swap them if out of order; larger values "bubble" toward the end.
  • fill(arr, arr + size, value) (from <algorithm>) fills a range of elements with one value.

Multidimensional Arrays

An array where each element is itself another array — great for grids/matrices (rows & columns).

string cars[3][3] = {
    {"Mustang", "Escape", "F150"},
    {"Corvette", "Equinox", "Silverado"},
    {"Challenger", "Durango", "Ram1500"}
};
cout << cars[0][0];   // "Mustang" (row 0, column 0)

Iterate with nested for loops — outer loop for rows, inner loop for columns:

for (int i = 0; i < rows; i++) {
    for (int j = 0; j < columns; j++) {
        cout << cars[i][j] << " ";
    }
    cout << endl;
}

Functions

A function is a reusable block of code that performs a specific task — write once, call many times.

void sayHi(string name, int age) {     // declaration/definition
    cout << "Hello " << name << ", you are " << age << endl;
}
 
int main() {
    sayHi("Mike", 60);                 // call/invoke — pass arguments
    return 0;
}
  • void = the function returns nothing.
  • Parameters are the placeholders in the function definition; arguments are the actual values passed when calling.
  • Functions can't see each other's local variables — pass what's needed as arguments.
  • If a function is defined after main(), add a function declaration (prototype) above main() so the compiler knows about it in advance:
void sayHi(string name, int age);   // prototype

Return Keyword

return sends a value back to wherever the function was called, and immediately exits the function.

double square(double length) {
    return length * length;
}
 
double area = square(6.0);   // 36
  • The function's return type (before the function name) must match the type of value returned (void if nothing is returned).
  • Any code after a return statement inside that path never executes.

Overloaded Functions

Multiple functions can share the same name as long as they have a different set of parameters (a different "function signature").

void bakePizza() { cout << "Here is your pizza"; }
void bakePizza(string topping) { cout << "Here is your " << topping << " pizza"; }
void bakePizza(string t1, string t2) { cout << "Here is your " << t1 << " and " << t2 << " pizza"; }

The compiler picks the correct version based on how many/what type of arguments you pass.


Variable Scope

  • Local variables — declared inside a function or { } block; only visible there. Different functions can reuse the same variable name safely.
  • Global variables — declared outside all functions (top of file); accessible everywhere. Generally avoid globals — they pollute the namespace and are less secure/predictable.
  • A local variable takes priority over a global one with the same name. Use the scope resolution operator ::variableName to explicitly access the global version.

Random Numbers

#include <cstdlib>
#include <ctime>
 
srand(time(0));                 // seed once (usually at program start)
int diceRoll = rand() % 6 + 1;  // random number 1–6

rand() % N gives 0 to N-1; add 1 (or a minimum value) to shift the range as needed.


Memory Addresses & Pointers

Every variable lives at a physical memory address in RAM. The address-of operator & retrieves that address.

int age = 21;
cout << &age;          // prints a hexadecimal memory address

A pointer is a variable that stores a memory address (instead of a normal value). Declared using the same data type as what it points to, plus *.

int age = 21;
int* pAge = &age;      // pAge holds the ADDRESS of age
cout << pAge;           // prints the address
cout << *pAge;           // DEREFERENCE: prints the VALUE at that address (21)
  • &variable → get the address (creates a pointer).
  • *pointer → dereference: get the value stored at that address.
  • Arrays are already addresses — you don't need & when pointing to one.
  • A null pointer (nullptr) points to nothing. Good practice: initialize pointers to nullptr if you're not assigning them right away, and check if (ptr != nullptr) before dereferencing to avoid undefined behavior.

Pass by Value vs Pass by Reference

  • Pass by value (default): the function receives a copy of the argument — modifying it inside the function does NOT affect the original.
  • Pass by reference: prefix the parameter with & — the function works with the actual original variable/memory address, so changes persist outside the function.
void swap(string &x, string &y) {   // pass by reference
    string temp = x;
    x = y;
    y = temp;
}

Use pass by reference whenever a function needs to modify the caller's original variable (e.g., swap functions), and generally prefer it for efficiency with large objects (avoids copying).


Const Parameters

Adding const before a parameter makes it read-only inside the function — the compiler blocks any attempt to modify it. Improves safety and communicates intent to other developers (especially important when combined with references/pointers, where an accidental modification would otherwise affect the original data).

void printInfo(const string &name, const int &age) {
    // name and age cannot be reassigned in here
}

Dynamic Memory

Memory allocated while the program is running (in the heap, not the fixed-size stack) — useful when you don't know how much storage you'll need in advance (e.g., depends on user input).

int* pNum = nullptr;
pNum = new int;          // allocate space for one int in the heap
*pNum = 21;
delete pNum;              // free the memory when done (prevents memory leaks)
 
// Dynamic array:
int size;
cin >> size;
char* pGrades = new char[size];
// ... use pGrades[i] ...
delete[] pGrades;         // note the [] when deleting an array

Rule of thumb: every new should eventually be matched with a delete (or delete[] for arrays) to avoid memory leaks.


Recursion

A function that calls itself to break a problem into smaller repeatable steps. Requires a base case to stop, or you'll get infinite recursion → stack overflow.

int factorial(int num) {
    if (num > 1) {
        return num * factorial(num - 1);   // recursive case
    } else {
        return 1;                          // base case
    }
}
  • Pros: often cleaner/easier to read for problems like tree traversal, sorting/searching algorithms.
  • Cons: uses more memory and is generally slower than an equivalent iterative (loop-based) solution.

Function Templates

A template lets you write one function that works with multiple data types, avoiding duplicate overloaded functions.

template <typename T>
T getMax(T x, T y) {
    return (x > y) ? x : y;
}
 
getMax(1, 2);        // works with int
getMax(1.1, 2.1);     // works with double
getMax('a', 'b');     // works with char

For mixing two different types, add a second template parameter and use auto as the return type so the compiler deduces the correct one:

template <typename T, typename U>
auto getMax(T x, U y) {
    return (x > y) ? x : y;
}

Structs

A struct groups related variables of different data types under one name (unlike arrays, which only hold one data type).

struct Student {
    string name;
    double gpa;
    bool enrolled = true;    // members can have default values
};
 
Student student1;
student1.name = "SpongeBob";
student1.gpa = 3.2;
cout << student1.name;
  • Members are accessed with the dot operator ..
  • Structs are passed by value by default (a copy is made) — use & in the parameter to pass by reference if you need to modify the original.
void printCar(Car &car) { cout << car.model; }   // pass by reference

Enums

enum (enumeration) is a user-defined type made of paired names and integer constants — great for representing a fixed set of options (and usable inside switch statements, unlike string).

enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY };
// If you don't assign values explicitly, they default to 0, 1, 2, 3...
 
Day today = FRIDAY;
 
switch (today) {
    case SUNDAY:  cout << "It is Sunday"; break;
    case FRIDAY:  cout << "It is Friday"; break;
    // ...
}

An enum variable can only take one value from its defined set.


Object-Oriented Programming

An object is a collection of:

  • Attributes (characteristics / data) — e.g. a phone has a version, charge level.
  • Methods (functions / actions it can perform) — e.g. a phone can make calls, play games.

A class is the blueprint used to create objects. An object is an actual instance of that blueprint.

class Human {
public:
    string name;
    string occupation;
    int age;
 
    void eat()   { cout << "This person is eating";   }
    void drink() { cout << "This person is drinking"; }
    void sleep() { cout << "This person is sleeping"; }
};
 
Human human1;
human1.name = "Rick";
human1.occupation = "Scientist";
human1.age = 70;
human1.eat();     // invoke a method
  • public: access modifier makes members accessible from outside the class.
  • You can create as many objects from one class as you like — each has its own independent copy of the attributes.

Constructors

A constructor is a special method (same name as the class, no return type) that runs automatically whenever an object is created — perfect for initializing attributes from arguments instead of assigning them manually one by one.

class Student {
public:
    string name;
    int age;
    double gpa;
 
    Student(string name, int age, double gpa) {
        this->name = name;     // "this" refers to the current object
        this->age = age;
        this->gpa = gpa;
    }
};
 
Student student1("SpongeBob", 25, 3.2);   // constructor called automatically
  • this->attribute disambiguates the class member from a same-named parameter.
  • Overloaded constructors: define multiple constructors with different parameter lists to allow flexible object creation (e.g., a pizza with 0, 1, or 2 toppings).
Pizza() {}                                       // no-topping constructor
Pizza(string topping1) { ... }                   // one topping
Pizza(string topping1, string topping2) { ... }  // two toppings

Object (Member) Functions

Functions defined inside a class that operate on that object's own data — great for computing derived information or performing an action using the object's attributes.

class Student {
public:
    double gpa;
    bool hasHonors() {
        if (gpa >= 3.5) return true;
        else return false;
    }
};
 
student1.hasHonors();   // uses student1's own gpa value

Changing the logic in one place (e.g. the honors GPA threshold) automatically updates behavior for every object of that class — a key benefit of OOP.


Getters & Setters

Used for abstraction/encapsulation — hiding internal data from direct outside access using private, and exposing controlled access through public methods.

class Movie {
private:
    string rating;    // hidden from outside code
 
public:
    void setRating(string r) {
        if (r == "G" || r == "PG" || r == "PG13" || r == "R" || r == "NR") {
            rating = r;
        } else {
            rating = "NR";   // reject invalid input, default safely
        }
    }
    string getRating() { return rating; }
};
 
Movie avengers;
avengers.setRating("PG13");   // must go through the setter
cout << avengers.getRating(); // must go through the getter
  • Getter: makes a private attribute readable.
  • Setter: makes a private attribute writable — and lets you add validation logic before allowing the change.
  • public members are accessible from anywhere; private members are only accessible from within the class itself.
  • Constructors can call setters internally to reuse validation logic.

Inheritance

A class (child/subclass) can inherit attributes and methods from another class (parent/superclass) — promotes code reuse and avoids repetition.

class Animal {
public:
    bool alive = true;
    void eat() { cout << "This animal is eating"; }
};
 
class Dog : public Animal {     // Dog inherits from Animal
public:
    void bark() { cout << "The dog goes woof"; }
};
 
Dog myDog;
myDog.eat();     // inherited from Animal
myDog.bark();    // defined in Dog
  • Syntax: class Child : public Parent { ... };
  • A subclass can add new members and override an inherited method by redefining it with the same signature.
  • If multiple classes share common attributes/methods, put them in a shared parent class so you only maintain that logic in one place.

Mini Practice Projects

(Referenced throughout the courses — good exercises to build once you know the fundamentals)

  • Hypotenuse calculator (math functions + user input)
  • Simple console calculator (switch statement)
  • Temperature converter (Celsius ⇄ Fahrenheit)
  • Number guessing game (do-while loop, random numbers)
  • Random event/prize generator (switch + random numbers)
  • Rock, Paper, Scissors game (functions, switch, random numbers)
  • Quiz game (2D arrays, arrays, scoring)
  • Credit card validator (Luhn algorithm — string/array manipulation)
  • Banking program (deposit/withdraw/balance — functions, do-while, validation)
  • Tic-Tac-Toe (pointers, arrays, win-condition checks, random computer moves)
  • Mad Libs generator (string input, string concatenation)
  • Basic 4-function calculator (if/else chains for operator selection)

Quick Reference Cheat Sheet

#include <iostream>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
using namespace std;
 
int main() {
    // Variables
    int age = 21;
    double gpa = 3.5;
    char grade = 'A';
    bool isStudent = true;
    string name = "Bro";
 
    // Output / Input
    cout << "Hello " << name << endl;
    cin >> age;
    getline(cin, name);
 
    // Conditionals
    if (age >= 18) { /* ... */ }
    else { /* ... */ }
 
    // Loop
    for (int i = 0; i < 10; i++) { /* ... */ }
 
    // Array
    int nums[5] = {1, 2, 3, 4, 5};
 
    // Function
    // int add(int a, int b) { return a + b; }
 
    return 0;
}

End of notes.