C++ is one of the most powerful programming languages ever created. It combines the low-level control of C with high-level abstractions, object-oriented programming, and the Standard Template Library (STL). C++ powers game engines (Unreal Engine), operating systems, browsers (Chrome's V8 engine core), databases (MySQL), and embedded systems. This tutorial takes you from zero to writing real C++ programs, step by step, with no prior C++ experience required.
What you'll learn
| Topic | What you'll be able to do |
|---|---|
| Setup | Install a C++ compiler and write your first program |
| Syntax & variables | Understand types, operators, and expressions |
| Control flow | Use if/else, switch, loops, and functions |
| OOP | Build classes, objects, inheritance, and polymorphism |
| Memory | Work with pointers, references, and dynamic memory |
| STL | Use vectors, maps, sets, and algorithms |
| Modern C++ | Apply C++11/14/17/20 features |
Why learn C++?
| Use Case | Examples |
|---|---|
| Game development | Unreal Engine, AAA game studios |
| Systems programming | Operating systems, drivers, compilers |
| High-performance software | Trading systems, scientific computing |
| Embedded / IoT | Microcontrollers, robotics, automotive |
| Graphics & engines | OpenGL, Vulkan, DirectX applications |
| Browsers & databases | Chrome, Firefox, MySQL, SQLite |
| Competitive programming | Most performance-critical contest solutions |
C++ vs other languages
| Feature | C++ | C | Java | Python | Rust |
|---|---|---|---|---|---|
| Performance | Excellent | Excellent | Good | Moderate | Excellent |
| Memory control | Manual | Manual | GC | GC | Ownership |
| OOP support | Full | None | Full | Full | Partial |
| Learning curve | Steep | Moderate | Moderate | Easy | Very steep |
| Ecosystem | Huge | Large | Huge | Huge | Growing |
| Use case | Systems/games/perf | Systems/embedded | Enterprise | Scripting/ML | Systems/safety |
Setup: Install a C++ compiler
Windows (MinGW-w64 via MSYS2)
# 1. Download and install MSYS2 from https://www.msys2.org/
# 2. In MSYS2 terminal:
pacman -S mingw-w64-ucrt-x86_64-gcc
# 3. Add C:\msys64\ucrt64\bin to PATH
# 4. Verify:
g++ --version
Windows (Visual Studio)
Download Visual Studio Community (free) — includes MSVC compiler. Select "Desktop development with C++".
macOS
# Install Xcode command line tools (includes Clang):
xcode-select --install
# Or install via Homebrew:
brew install gcc
clang++ --version
Linux (Ubuntu/Debian)
sudo apt update
sudo apt install build-essential g++
g++ --version
Recommended editor: VS Code
Install VS Code + the C/C++ extension (Microsoft). For a full IDE: CLion (JetBrains) or Visual Studio (Windows).
Hello, World!
#include <iostream>
int main() {
std::cout << "Hello, World!" << std::endl;
return 0;
}
Compile and run:
g++ hello.cpp -o hello
./hello # Linux/macOS
hello.exe # Windows
What each line means
#include <iostream>— include the input/output libraryint main()— entry point of every C++ programstd::cout << "Hello, World!" << std::endl;— print to consolereturn 0;— signal success to the OS
Variables and data types
Basic types
#include <iostream>
#include <string>
int main() {
// Integer types
int age = 25;
long population = 8000000000L;
short small = 100;
// Floating point
double pi = 3.14159265;
float approx = 3.14f;
// Boolean
bool isActive = true;
// Character
char grade = 'A';
// String (from <string>)
std::string name = "Alice";
std::cout << name << " is " << age << " years old\n";
return 0;
}
Type sizes (platform-dependent, 64-bit typical)
| Type | Size | Range |
|---|---|---|
bool |
1 byte | true / false |
char |
1 byte | -128 to 127 |
int |
4 bytes | -2.1B to 2.1B |
long long |
8 bytes | ±9.2 quintillion |
float |
4 bytes | ~7 significant digits |
double |
8 bytes | ~15 significant digits |
Use int for most integers, double for most decimals, std::string for text.
auto keyword (C++11)
auto x = 42; // int
auto y = 3.14; // double
auto s = std::string("hello"); // std::string
auto flag = true; // bool
Constants
const double PI = 3.14159265358979;
constexpr int MAX_SIZE = 1000; // compile-time constant (prefer this)
Strings
#include <iostream>
#include <string>
int main() {
std::string s = "Hello, C++!";
// Length
std::cout << s.length() << "\n"; // 11
// Concatenation
std::string full = "Hello" + std::string(", World!");
// Substring
std::cout << s.substr(7, 3) << "\n"; // C++
// Find
size_t pos = s.find("C++");
if (pos != std::string::npos) {
std::cout << "Found at " << pos << "\n"; // 7
}
// Replace
s.replace(7, 3, "World");
std::cout << s << "\n"; // Hello, World!
// Access individual characters
std::cout << s[0] << "\n"; // H
return 0;
}
String methods quick reference
| Method | Purpose |
|---|---|
s.length() / s.size() |
Number of characters |
s.empty() |
True if string is empty |
s.substr(pos, len) |
Extract substring |
s.find(sub) |
Find first occurrence (npos if not found) |
s.replace(pos, len, str) |
Replace portion |
s.append(str) / s += str |
Append |
s.erase(pos, len) |
Remove portion |
s.at(i) |
Access with bounds checking |
s.c_str() |
Convert to C-style const char* |
Control flow
if / else
int score = 85;
if (score >= 90) {
std::cout << "A\n";
} else if (score >= 80) {
std::cout << "B\n";
} else if (score >= 70) {
std::cout << "C\n";
} else {
std::cout << "F\n";
}
Ternary operator
int x = 10;
std::string result = (x > 0) ? "positive" : "non-positive";
switch
char grade = 'B';
switch (grade) {
case 'A':
std::cout << "Excellent\n";
break;
case 'B':
std::cout << "Good\n";
break;
case 'C':
std::cout << "Average\n";
break;
default:
std::cout << "Unknown\n";
}
Loops
// for loop
for (int i = 0; i < 5; i++) {
std::cout << i << " ";
}
// 0 1 2 3 4
// while loop
int n = 10;
while (n > 0) {
std::cout << n << " ";
n -= 3;
}
// 10 7 4 1
// do-while (runs at least once)
int count = 0;
do {
count++;
} while (count < 5);
// Range-based for (C++11) — works on arrays, vectors, strings
std::vector<int> nums = {1, 2, 3, 4, 5};
for (int n : nums) {
std::cout << n << " ";
}
// break and continue
for (int i = 0; i < 10; i++) {
if (i == 5) break;
if (i % 2 == 0) continue;
std::cout << i << " "; // 1 3
}
Functions
#include <iostream>
// Function declaration (prototype)
int add(int a, int b);
double power(double base, int exp = 2); // default parameter
// Function definitions
int add(int a, int b) {
return a + b;
}
double power(double base, int exp) {
double result = 1.0;
for (int i = 0; i < exp; i++) {
result *= base;
}
return result;
}
// Multiple return values via reference parameters
void minMax(int arr[], int size, int& minVal, int& maxVal) {
minVal = maxVal = arr[0];
for (int i = 1; i < size; i++) {
if (arr[i] < minVal) minVal = arr[i];
if (arr[i] > maxVal) maxVal = arr[i];
}
}
// Function overloading
double add(double a, double b) { return a + b; }
int main() {
std::cout << add(3, 4) << "\n"; // 7
std::cout << add(1.5, 2.5) << "\n"; // 4.0
std::cout << power(2.0) << "\n"; // 4.0
std::cout << power(2.0, 3) << "\n"; // 8.0
int arr[] = {5, 2, 8, 1, 9, 3};
int lo, hi;
minMax(arr, 6, lo, hi);
std::cout << lo << " " << hi << "\n"; // 1 9
return 0;
}
Pass by value vs reference vs pointer
void byValue(int x) { x = 100; } // caller unchanged
void byRef(int& x) { x = 100; } // modifies caller's variable
void byPtr(int* x) { *x = 100; } // modifies via pointer
int n = 5;
byValue(n); // n still 5
byRef(n); // n is now 100
byPtr(&n); // n is now 100
Arrays and vectors
C-style arrays (fixed size)
int scores[5] = {90, 85, 78, 92, 88};
int size = sizeof(scores) / sizeof(scores[0]); // 5
for (int i = 0; i < size; i++) {
std::cout << scores[i] << " ";
}
std::vector (dynamic array — prefer this)
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> v = {3, 1, 4, 1, 5, 9};
// Add / remove
v.push_back(2); // {3,1,4,1,5,9,2}
v.pop_back(); // {3,1,4,1,5,9}
v.insert(v.begin(), 0); // {0,3,1,4,1,5,9}
v.erase(v.begin()); // {3,1,4,1,5,9}
// Size info
std::cout << v.size() << "\n"; // 6
std::cout << v.empty() << "\n"; // 0 (false)
// Sort
std::sort(v.begin(), v.end()); // {1,1,3,4,5,9}
// Range-based for
for (int x : v) {
std::cout << x << " ";
}
// Find
auto it = std::find(v.begin(), v.end(), 4);
if (it != v.end()) {
std::cout << "\nFound 4 at index " << (it - v.begin()) << "\n";
}
return 0;
}
2D vector
std::vector<std::vector<int>> matrix(3, std::vector<int>(3, 0));
matrix[1][1] = 5;
Pointers and references
Pointers are one of C++'s most distinctive (and initially confusing) features. They store memory addresses.
#include <iostream>
int main() {
int x = 42;
// Reference: alias for x
int& ref = x;
ref = 100; // x is now 100
// Pointer: stores address of x
int* ptr = &x; // & gets address of x
std::cout << ptr << "\n"; // memory address e.g. 0x7fff...
std::cout << *ptr << "\n"; // dereference: 100
*ptr = 200; // x is now 200
// Null pointer
int* np = nullptr; // don't use NULL in modern C++
if (np == nullptr) {
std::cout << "null pointer\n";
}
return 0;
}
Dynamic memory
// Allocate on heap
int* p = new int(42);
std::cout << *p << "\n"; // 42
delete p; // must free!
p = nullptr; // good practice
// Array
int* arr = new int[10];
arr[0] = 1;
delete[] arr; // use delete[] for arrays!
Modern C++ alternative: use smart pointers instead of raw new/delete.
Object-oriented programming (OOP)
Classes and objects
#include <iostream>
#include <string>
class Animal {
private:
std::string name;
int age;
public:
// Constructor
Animal(const std::string& name, int age)
: name(name), age(age) {}
// Destructor
~Animal() {
// cleanup if needed
}
// Getters
std::string getName() const { return name; }
int getAge() const { return age; }
// Method
virtual void speak() const {
std::cout << name << " makes a sound\n";
}
// toString equivalent
friend std::ostream& operator<<(std::ostream& os, const Animal& a) {
os << "Animal(" << a.name << ", age=" << a.age << ")";
return os;
}
};
int main() {
Animal cat("Whiskers", 3);
std::cout << cat.getName() << "\n"; // Whiskers
cat.speak(); // Whiskers makes a sound
std::cout << cat << "\n"; // Animal(Whiskers, age=3)
return 0;
}
Inheritance
class Dog : public Animal {
private:
std::string breed;
public:
Dog(const std::string& name, int age, const std::string& breed)
: Animal(name, age), breed(breed) {}
// Override virtual method
void speak() const override {
std::cout << getName() << " says: Woof!\n";
}
std::string getBreed() const { return breed; }
};
class Cat : public Animal {
public:
Cat(const std::string& name, int age) : Animal(name, age) {}
void speak() const override {
std::cout << getName() << " says: Meow!\n";
}
};
Polymorphism
#include <vector>
#include <memory>
int main() {
// Polymorphism via pointers/references
std::vector<std::unique_ptr<Animal>> animals;
animals.push_back(std::make_unique<Dog>("Rex", 5, "Labrador"));
animals.push_back(std::make_unique<Cat>("Luna", 2));
for (const auto& a : animals) {
a->speak(); // calls correct speak() based on actual type
}
// Rex says: Woof!
// Luna says: Meow!
return 0;
}
Access modifiers
| Modifier | Same class | Derived class | Outside |
|---|---|---|---|
public |
Yes | Yes | Yes |
protected |
Yes | Yes | No |
private |
Yes | No | No |
Interfaces via abstract classes
class Shape {
public:
virtual double area() const = 0; // pure virtual
virtual double perimeter() const = 0;
virtual ~Shape() = default; // always virtual destructor
};
class Circle : public Shape {
double radius;
public:
Circle(double r) : radius(r) {}
double area() const override { return 3.14159 * radius * radius; }
double perimeter() const override { return 2 * 3.14159 * radius; }
};
class Rectangle : public Shape {
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
double area() const override { return width * height; }
double perimeter() const override { return 2 * (width + height); }
};
The four pillars of OOP
| Pillar | What it means | C++ mechanism |
|---|---|---|
| Encapsulation | Hide internal state | private/protected members |
| Abstraction | Expose only necessary interface | Abstract classes, interfaces |
| Inheritance | Reuse and extend existing classes | : public BaseClass |
| Polymorphism | Same call, different behavior | virtual / override |
Smart pointers (Modern C++)
Raw new/delete is error-prone. Use smart pointers from <memory>.
#include <memory>
#include <iostream>
class Resource {
public:
Resource() { std::cout << "Resource acquired\n"; }
~Resource() { std::cout << "Resource released\n"; }
void use() { std::cout << "Using resource\n"; }
};
int main() {
// unique_ptr: sole ownership, auto-delete when out of scope
{
auto res = std::make_unique<Resource>();
res->use();
} // ~Resource() called here automatically
// shared_ptr: shared ownership, delete when last owner leaves scope
auto sp1 = std::make_shared<Resource>();
{
auto sp2 = sp1; // ref count = 2
sp2->use();
} // ref count = 1, resource not deleted yet
// ref count = 0 here, resource deleted
return 0;
}
| Smart pointer | Ownership | Use when |
|---|---|---|
unique_ptr |
Exclusive | Most cases — sole owner |
shared_ptr |
Shared | Multiple owners needed |
weak_ptr |
Non-owning | Break circular references |
Rule: prefer unique_ptr. Use shared_ptr only when you need shared ownership. Avoid raw new/delete.
The Standard Template Library (STL)
Containers
#include <vector>
#include <list>
#include <map>
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <stack>
#include <queue>
std::map (sorted key-value)
#include <map>
#include <iostream>
int main() {
std::map<std::string, int> wordCount;
wordCount["hello"]++;
wordCount["world"]++;
wordCount["hello"]++;
// Iterate (sorted by key)
for (const auto& [word, count] : wordCount) {
std::cout << word << ": " << count << "\n";
}
// hello: 2
// world: 1
// Check existence
if (wordCount.count("hello")) {
std::cout << "Found hello\n";
}
wordCount.erase("world");
std::cout << wordCount.size() << "\n"; // 1
return 0;
}
std::unordered_map (hash map — O(1) average)
#include <unordered_map>
std::unordered_map<std::string, int> freq;
freq["apple"] = 5;
freq["banana"] = 3;
std::cout << freq["apple"] << "\n"; // 5
Use unordered_map when order doesn't matter — it's faster than map.
std::set and std::unordered_set
#include <set>
std::set<int> s = {3, 1, 4, 1, 5, 9}; // duplicates removed, sorted
// s = {1, 3, 4, 5, 9}
s.insert(2);
s.erase(3);
if (s.count(4)) {
std::cout << "4 is in the set\n";
}
std::stack and std::queue
#include <stack>
#include <queue>
std::stack<int> stk;
stk.push(1); stk.push(2); stk.push(3);
while (!stk.empty()) {
std::cout << stk.top() << " "; // 3 2 1
stk.pop();
}
std::queue<int> q;
q.push(1); q.push(2); q.push(3);
while (!q.empty()) {
std::cout << q.front() << " "; // 1 2 3
q.pop();
}
STL algorithms
#include <algorithm>
#include <vector>
#include <numeric>
std::vector<int> v = {5, 3, 8, 1, 9, 2};
// Sort
std::sort(v.begin(), v.end()); // ascending
std::sort(v.begin(), v.end(), std::greater<>()); // descending
// Find
auto it = std::find(v.begin(), v.end(), 8);
bool found = (it != v.end());
// Count
int cnt = std::count(v.begin(), v.end(), 3);
// Sum
int total = std::accumulate(v.begin(), v.end(), 0);
// Min/max
auto [mn, mx] = std::minmax_element(v.begin(), v.end());
std::cout << *mn << " " << *mx << "\n";
// Reverse
std::reverse(v.begin(), v.end());
// Filter (copy_if)
std::vector<int> evens;
std::copy_if(v.begin(), v.end(), std::back_inserter(evens),
[](int x) { return x % 2 == 0; });
Container comparison
| Container | Access | Insert/Delete | Ordered | Use case |
|---|---|---|---|---|
vector |
O(1) | O(n) / O(1) end | No | General array |
list |
O(n) | O(1) anywhere | No | Frequent middle insert |
deque |
O(1) | O(1) both ends | No | Double-ended queue |
map |
O(log n) | O(log n) | Yes | Sorted key-value |
unordered_map |
O(1) avg | O(1) avg | No | Fast lookup |
set |
O(log n) | O(log n) | Yes | Unique sorted values |
stack |
Top only | O(1) | No | LIFO |
queue |
Front only | O(1) | No | FIFO |
Modern C++ features (C++11 to C++20)
Lambda expressions (C++11)
#include <algorithm>
#include <vector>
#include <iostream>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5, 6};
// Inline function
auto square = [](int x) { return x * x; };
std::cout << square(4) << "\n"; // 16
// Sort descending with lambda
std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; });
// Capture outer variable
int threshold = 3;
std::vector<int> big;
std::copy_if(v.begin(), v.end(), std::back_inserter(big),
[threshold](int x) { return x > threshold; });
// big = {4, 5, 6}
return 0;
}
Range-based for with structured bindings (C++17)
std::map<std::string, int> scores = {{"Alice", 95}, {"Bob", 87}};
for (const auto& [name, score] : scores) {
std::cout << name << ": " << score << "\n";
}
Initializer lists and uniform initialization (C++11)
std::vector<int> v{1, 2, 3, 4, 5}; // brace initialization
int arr[]{10, 20, 30};
nullptr (C++11)
Always use nullptr instead of NULL or 0 for null pointers.
Move semantics (C++11)
std::vector<int> source = {1, 2, 3, 4, 5};
std::vector<int> dest = std::move(source); // no copy — transfers ownership
// source is now empty, dest has the data
constexpr functions (C++11)
constexpr int factorial(int n) {
return (n <= 1) ? 1 : n * factorial(n - 1);
}
constexpr int f5 = factorial(5); // computed at compile time: 120
std::optional (C++17)
#include <optional>
std::optional<int> divide(int a, int b) {
if (b == 0) return std::nullopt;
return a / b;
}
auto result = divide(10, 2);
if (result) {
std::cout << *result << "\n"; // 5
}
Ranges (C++20)
#include <ranges>
#include <vector>
#include <iostream>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8};
// Filter + transform with pipe syntax
for (int x : v | std::views::filter([](int n){ return n % 2 == 0; })
| std::views::transform([](int n){ return n * n; })) {
std::cout << x << " "; // 4 16 36 64
}
return 0;
}
Error handling
Exceptions
#include <iostream>
#include <stdexcept>
double safeDivide(double a, double b) {
if (b == 0.0) {
throw std::invalid_argument("Division by zero");
}
return a / b;
}
int main() {
try {
std::cout << safeDivide(10.0, 2.0) << "\n"; // 5
std::cout << safeDivide(10.0, 0.0) << "\n"; // throws
} catch (const std::invalid_argument& e) {
std::cerr << "Error: " << e.what() << "\n";
} catch (const std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
Custom exceptions
class ValidationError : public std::exception {
std::string message;
public:
explicit ValidationError(const std::string& msg) : message(msg) {}
const char* what() const noexcept override {
return message.c_str();
}
};
Common standard exceptions
| Exception | When to use |
|---|---|
std::invalid_argument |
Invalid argument to function |
std::out_of_range |
Index out of bounds |
std::runtime_error |
General runtime error |
std::overflow_error |
Arithmetic overflow |
std::logic_error |
Logic bug (precondition violated) |
std::bad_alloc |
Memory allocation failure |
File I/O
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
int main() {
// Write to file
{
std::ofstream out("data.txt");
if (!out) {
std::cerr << "Cannot open file\n";
return 1;
}
out << "Line 1\n";
out << "Line 2\n";
} // file auto-closed here (RAII)
// Read entire file
{
std::ifstream in("data.txt");
std::string line;
while (std::getline(in, line)) {
std::cout << line << "\n";
}
}
// Read into string all at once
{
std::ifstream in("data.txt");
std::ostringstream buffer;
buffer << in.rdbuf();
std::string content = buffer.str();
std::cout << content;
}
// Append
{
std::ofstream out("data.txt", std::ios::app);
out << "Line 3\n";
}
return 0;
}
Templates (generics)
Templates let you write type-independent code.
#include <iostream>
#include <vector>
#include <algorithm>
// Function template
template <typename T>
T maxOf(T a, T b) {
return (a > b) ? a : b;
}
// Class template
template <typename T>
class Stack {
std::vector<T> data;
public:
void push(const T& val) { data.push_back(val); }
void pop() { data.pop_back(); }
T& top() { return data.back(); }
bool empty() const { return data.empty(); }
size_t size() const { return data.size(); }
};
int main() {
std::cout << maxOf(3, 7) << "\n"; // 7
std::cout << maxOf(3.14, 2.71) << "\n"; // 3.14
std::cout << maxOf('a', 'z') << "\n"; // z
Stack<int> s;
s.push(1); s.push(2); s.push(3);
std::cout << s.top() << "\n"; // 3
s.pop();
std::cout << s.top() << "\n"; // 2
return 0;
}
3 Real projects
Project 1: Command-line To-Do list
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
struct Task {
int id;
std::string title;
bool done;
};
class TodoList {
std::vector<Task> tasks;
int nextId = 1;
public:
void add(const std::string& title) {
tasks.push_back({nextId++, title, false});
std::cout << "Added: " << title << "\n";
}
void complete(int id) {
for (auto& t : tasks) {
if (t.id == id) {
t.done = true;
std::cout << "Completed: " << t.title << "\n";
return;
}
}
std::cout << "Task " << id << " not found\n";
}
void remove(int id) {
auto it = std::remove_if(tasks.begin(), tasks.end(),
[id](const Task& t){ return t.id == id; });
if (it != tasks.end()) {
tasks.erase(it, tasks.end());
std::cout << "Removed task " << id << "\n";
}
}
void list() const {
if (tasks.empty()) {
std::cout << "No tasks.\n";
return;
}
for (const auto& t : tasks) {
std::cout << "[" << (t.done ? "X" : " ") << "] "
<< t.id << ". " << t.title << "\n";
}
}
};
int main() {
TodoList todo;
std::string cmd;
std::cout << "Commands: add <title> | done <id> | rm <id> | list | quit\n";
while (std::getline(std::cin, cmd)) {
if (cmd == "quit") break;
else if (cmd == "list") todo.list();
else if (cmd.substr(0, 3) == "add") todo.add(cmd.substr(4));
else if (cmd.substr(0, 4) == "done") todo.complete(std::stoi(cmd.substr(5)));
else if (cmd.substr(0, 2) == "rm") todo.remove(std::stoi(cmd.substr(3)));
else std::cout << "Unknown command\n";
}
return 0;
}
Project 2: Word frequency counter
#include <iostream>
#include <fstream>
#include <sstream>
#include <unordered_map>
#include <vector>
#include <algorithm>
#include <cctype>
std::string normalize(std::string word) {
// lowercase and remove punctuation
std::string result;
for (char c : word) {
if (std::isalpha(c)) result += std::tolower(c);
}
return result;
}
int main(int argc, char* argv[]) {
std::string text;
if (argc > 1) {
std::ifstream in(argv[1]);
std::ostringstream buf;
buf << in.rdbuf();
text = buf.str();
} else {
std::cout << "Enter text (Ctrl+D to finish):\n";
std::ostringstream buf;
buf << std::cin.rdbuf();
text = buf.str();
}
std::unordered_map<std::string, int> freq;
std::istringstream iss(text);
std::string token;
while (iss >> token) {
std::string word = normalize(token);
if (!word.empty()) freq[word]++;
}
// Sort by frequency descending
std::vector<std::pair<std::string, int>> sorted(freq.begin(), freq.end());
std::sort(sorted.begin(), sorted.end(),
[](const auto& a, const auto& b){ return a.second > b.second; });
std::cout << "\nTop 10 words:\n";
int limit = std::min((int)sorted.size(), 10);
for (int i = 0; i < limit; i++) {
std::cout << sorted[i].first << ": " << sorted[i].second << "\n";
}
return 0;
}
# Compile and run:
g++ -std=c++17 wordfreq.cpp -o wordfreq
echo "the quick brown fox the fox" | ./wordfreq
Project 3: Student grade calculator
#include <iostream>
#include <vector>
#include <string>
#include <numeric>
#include <algorithm>
#include <iomanip>
struct Student {
std::string name;
std::vector<double> grades;
double average() const {
if (grades.empty()) return 0.0;
return std::accumulate(grades.begin(), grades.end(), 0.0) / grades.size();
}
char letterGrade() const {
double avg = average();
if (avg >= 90) return 'A';
if (avg >= 80) return 'B';
if (avg >= 70) return 'C';
if (avg >= 60) return 'D';
return 'F';
}
};
void printReport(const std::vector<Student>& students) {
std::cout << "\n" << std::setw(15) << std::left << "Name"
<< std::setw(10) << "Average"
<< "Grade\n";
std::cout << std::string(30, '-') << "\n";
for (const auto& s : students) {
std::cout << std::setw(15) << std::left << s.name
<< std::setw(10) << std::fixed << std::setprecision(1) << s.average()
<< s.letterGrade() << "\n";
}
// Class statistics
double classAvg = 0;
for (const auto& s : students) classAvg += s.average();
classAvg /= students.size();
auto best = std::max_element(students.begin(), students.end(),
[](const Student& a, const Student& b){ return a.average() < b.average(); });
std::cout << "\nClass average: " << std::fixed << std::setprecision(1) << classAvg << "\n";
std::cout << "Top student: " << best->name << " (" << best->average() << ")\n";
}
int main() {
std::vector<Student> students = {
{"Alice", {92, 88, 95, 90}},
{"Bob", {75, 82, 68, 79}},
{"Carol", {98, 95, 97, 99}},
{"Dave", {60, 55, 70, 65}},
};
printReport(students);
return 0;
}
Learning path
| Stage | Duration | Focus |
|---|---|---|
| 1. Syntax basics | 2–3 weeks | Variables, types, control flow, functions |
| 2. OOP | 2–3 weeks | Classes, inheritance, polymorphism |
| 3. Memory & pointers | 2 weeks | Pointers, references, smart pointers |
| 4. STL | 2 weeks | Containers, iterators, algorithms |
| 5. Modern C++ (11/14/17) | 2 weeks | Lambdas, move semantics, templates |
| 6. Real projects | Ongoing | Build something: game, tool, library |
| 7. Advanced topics | Ongoing | Concurrency, networking, performance, C++20 |
Free resources
| Resource | Best for |
|---|---|
| learncpp.com | Most complete free C++ tutorial |
| cppreference.com | Authoritative STL/language reference |
| Stroustrup's A Tour of C++ | Fast overview by the language creator |
| Effective Modern C++ (Meyers) | Essential modern C++ best practices |
| Compiler Explorer (godbolt.org) | See what your C++ compiles to |
| C++ Core Guidelines | Official best practices from Stroustrup+Sutter |
Common mistakes
| Mistake | Problem | Fix |
|---|---|---|
Using NULL instead of nullptr |
Type safety issues | Always use nullptr |
Forgetting delete / delete[] |
Memory leak | Use smart pointers (unique_ptr) |
Mixing delete and delete[] |
Undefined behavior | Match new[] with delete[] |
| Returning reference to local variable | Dangling reference | Return by value or use heap |
Comparing floating point with == |
Precision error | Use std::abs(a - b) < epsilon |
Not marking methods const |
Limits usability | Mark getters and read-only methods const |
| Raw loops over STL algorithms | More error-prone | Prefer std::sort, std::find, std::count |
Using using namespace std; in headers |
Name collisions | Use std:: prefix or limit to .cpp scope |
C++ vs related terms
| Term | What it is |
|---|---|
| C++ | Superset of C with OOP, templates, STL |
| C | Low-level predecessor; no classes or templates |
| g++ | GNU C++ compiler (Linux/macOS/Windows via MinGW) |
| clang++ | LLVM-based C++ compiler; excellent error messages |
| MSVC | Microsoft Visual C++ compiler (Windows) |
| CMake | Build system generator for C++ projects |
| STL | Standard Template Library — containers, algorithms, iterators |
| Boost | Popular C++ library collection, many features adopted into STL |
| LLVM | Compiler infrastructure; powers clang |
| Qt | Cross-platform C++ GUI framework |
Frequently asked questions
Is C++ still worth learning in 2025?
Absolutely. C++ is essential for game development (Unreal Engine), systems programming, embedded systems, high-frequency trading, browsers, and competitive programming. It consistently ranks in the top 5 languages by job market demand and offers unmatched performance.
Should I learn C before C++?
Not necessary. C++ is a separate language that includes C as a subset. You can learn C++ directly. Understanding C becomes useful later when working on embedded systems or reading legacy code.
What is the difference between C++11, C++14, C++17, and C++20?
These are ISO standard versions. C++11 was the major modernization (lambdas, smart pointers, move semantics, auto, range-based for). C++14 and C++17 added refinements. C++20 added concepts, ranges, coroutines, and modules. Compile with -std=c++17 or -std=c++20 to enable these features.
What's the difference between struct and class in C++?
Only the default access modifier: struct members are public by default, class members are private by default. Convention: use struct for passive data (no invariants), class for objects with behavior.
How do I compile a C++ program?g++ -std=c++17 myfile.cpp -o myprogram for most cases. Use -Wall -Wextra to enable warnings. For production, add -O2 for optimization.
When should I use C++ vs Python vs Rust?
Use C++ when you need maximum performance, game development (Unreal), or integrating with existing C++ codebases. Use Python for scripting, data science, and fast prototyping. Use Rust when you need C++ performance with memory safety guarantees and are starting a new project.