Cheatsheet C++
Linguagem de alto desempenho com controlo de memória
C++
Basic Syntax
Variables and types
int age = 30; double price = 9.99; char letter = 'A'; bool active = true; std::string name = "Anna"; auto x = 42; // type inferido (int) unsigned int counter = 0; long long big = 9999999999LL;
int, double, char, bool and std::string are the most used types. auto lets the compiler infer the type. unsigned only accepts positive values.
Input and output (I/O)
#include <iostream>
int main() {
std::cout << "Name: ";
std::string name;
std::getline(std::cin, name); // line completa
int age;
std::cout << "Age: ";
std::cin >> age;
std::cout << name << " tem " << age << " years\n";
return 0;
}std::cout prints, std::cin reads. << chains outputs. std::getline reads a whole line (with spaces). std::endl flushes — prefer "\n" for performance.
using and typedef
// Alias de type (moderno):
using Vec = std::vector<int>;
using Matrix = std::vector<std::vector<double>>;
using Callback = std::function<void(int)>;
Vec v = {1, 2, 3};
// typedef (legado):
typedef unsigned long ulong;
// Template alias:
template <typename T>
using Ptr = std::shared_ptr<T>;
Ptr<int> p = std::make_shared<int>(42);using creates type aliases (more readable than typedef). It supports templates. Useful for simplifying long types like std::vector<std::vector<double>>. Standard in modern C++.
Fixed-size types
#include <cstdint> int8_t a = -128; // 1 byte uint8_t b = 255; // 1 byte without signal int16_t c = -32768; // 2 bytes int32_t d = 2000000; // 4 bytes int64_t e = 9e18; // 8 bytes uint64_t f = 18e18; // 8 bytes without signal size_t n = 0; // size (unsigned)
Types from <cstdint> guarantee an exact size on any platform. int32_t = 4 bytes always. size_t is used for sizes and indexes. Prefer these when the size matters.
Strings (std::string)
#include <string>
std::string s = "Hello World";
s.length(); // 10
s.substr(4, 5); // "World"
s.find("World"); // 4 (position)
s.replace(0, 3, "Hi"); // "Hi World"
s += "!!"; // concatenation
s.empty(); // false
s.compare("Hi World!!"); // 0 if equal
// Formatting (C++20):
std::string r = std::format("{} has {}", name, age);std::string is dynamic and safe. substr extracts, find searches (returns npos if not found). std::format (C++20) formats like Python. Avoid C-strings (char*).
Structure of a program
#include <iostream>
#include <vector>
#include <string>
// Forward declarations:
void process(const std::string& s);
int main() {
std::vector<std::string> data = {"a", "b"};
for (const auto& d : data) {
process(d);
}
return 0; // success
}
void process(const std::string& s) {
std::cout << s << "\n";
}Every C++ program has main() the the entry point. #include imports headers. return 0 indicates success. Functions can be declared before and defined later. Compile with g++ main.cpp -o app.
Constants
const int MAX = 100; // immutable em runtime
constexpr double PI = 3.14159; // avaliada em compilation
constexpr int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
inline constexpr int TAM = 1024;
// enum class (tipado, without pollution):
enum class Color { Red, Green, Blue };
Color c = Color::Green;const prevents modification. constexpr is evaluated at compile time (faster). enum class is typed and does not pollute the namespace (use Color::Green instead of just Green).
Type conversions
// Numeric → string:
std::string s = std::to_string(42);
std::string d = std::to_string(3.14);
// String → numeric:
int n = std::stoi("123");
double x = std::stod("3.14");
long l = std::stol("999999");
// Cast explicit:
int i = static_cast<int>(3.99); // 3 (truncates)
double dd = static_cast<double>(7) / 2; // 3.5
// dynamic_cast (polymorphism):
Base* b = dynamic_cast<Derived*>(ptr);std::to_string converts number→string. std::stoi/std::stod do the reverse. static_cast is the safe compile-time cast. dynamic_cast checks at runtime (inheritance).
Operators
// Arithmetic: int r = 10 + 3; // 13 int q = 10 / 3; // 3 (integer!) int m = 10 % 3; // 1 (remainder) // Comparison: bool b = (5 == 5); // true bool c = (5 != 3); // true // Logical: bool d = (true && false); // false bool e = (true || false); // true // Bitwise: int f = 0b1010 & 0b1100; // 0b1000 int g = 0b1010 | 0b1100; // 1110
Arithmetic, comparison, logical and bitwise operators. Careful: 10 / 3 = 3 (integer division). % gives the remainder. && and || are short-circuit.
Arrays
// Array fixo (C-style):
int arr[5] = {1, 2, 3, 4, 5};
arr[0]; // 1
// std::array (prefer, C++11):
#include <array>
std::array<int, 5> a = {10, 20, 30, 40, 50};
a.size(); // 5
a.front(); // 10
a.back(); // 50
// Percorrer:
for (int x : a) std::cout << x << " ";std::array is fixed but safe (it knows its size). C arrays (int arr[5]) do not know their own size. For a dynamic size, use std::vector. std::array does not allocate on the heap.
Flow Control
if / else if / else
if (age >= 18) {
std::cout << "adult\n";
} else if (age >= 13) {
std::cout << "teenager\n";
} else {
std::cout << "child\n";
}
// Sem keys (only 1 statement):
if (x > 0) std::cout << "positive";if evaluates a boolean condition. else if chains checks. Braces are optional for 1 statement but always recommended. C++ has no elif — use else if.
while and do-while
// while: checks BEFORE executing
int n = 5;
while (n > 0) {
std::cout << n-- << " ";
}
// do-while: runs AT LEAST once
int option;
do {
std::cout << "Menu: ";
std::cin >> option;
} while (option != 0);while repeats while the condition is true. do-while guarantees at least 1 execution (checks at the end). Ideal for menus and input with validation.
Nested loops
// Matrix 3x3:
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
std::cout << matrix[i][j] << " ";
}
std::cout << "\n";
}
// Com range-for:
for (const auto& line : matrix) {
for (int val : line) {
std::cout << val << " ";
}
std::cout << "\n";
}Nested loops traverse 2D structures (matrices, grids). The outer loop controls rows, the inner one columns. A nested range-for is more readable. Be careful with performance in O(n²).
switch / case
switch (day) {
case 1:
std::cout << "Monday";
break;
case 2:
case 3:
std::cout << "Ter/Qua";
break;
default:
std::cout << "Other";
break;
}switch compares against integer constants. break avoids fall-through. Cases can share code (2 and 3). default is the fallback. Faster than if-else for many cases (jump table).
break and continue
for (int i = 0; i < 100; i++) {
if (i == 50) break; // sai do ciclo
if (i % 2 == 0) continue; // skips iteration
std::cout << i << " "; // only odd up to 49
}
// break em switch:
switch (x) {
case 1: doWork(); break; // without break = fall-through
}break ends the loop immediately. continue jumps to the next iteration. In a switch, break avoids executing the next case. Use it sparingly for readability.
goto and labels
// Use legitimate: exit de loops nested
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (found(i, j)) {
goto end;
}
}
}
end:
std::cout << "Search finished\n";
// AVOID: makes code unreadable
// Prefer: break com flag, or func com returngoto jumps to a label. The only acceptable use: exiting deeply nested loops. In general, avoid it — it makes the flow hard to follow. Prefer return, break or refactoring into a function.
Classic for
for (int i = 0; i < 10; i++) {
std::cout << i << " ";
}
// Multiple variables:
for (int i = 0, j = 10; i < j; i++, j--) {
std::cout << i << "," << j << " ";
}
// Decrescente:
for (int i = 9; i >= 0; i--) {
std::cout << i << " ";
}for has 3 parts: initialization, condition, increment. i++ is post-increment. It accepts multiple comma-separated variables. Ideal when you know the number of iterations.
Ternary operator
// condition ? value_if_true : value_if_false
int largest = (a > b) ? a : b;
std::string state = active ? "online" : "offline";
// Chained (avoid, barely readable):
char grade = (p >= 90) ? 'A'
: (p >= 80) ? 'B'
: (p >= 70) ? 'C' : 'F';
// With cout:
std::cout << (even ? "even" : "odd");The ?: operator is an if-else in an expression. It returns a value. Ideal for simple assignments. Avoid chaining (unreadable). The parentheses in (cond) are optional but help.
Range-based for (C++11)
std::vector<int> nums = {10, 20, 30};
// Por value (copy):
for (int x : nums) std::cout << x;
// Por reference (modifica):
for (int& x : nums) x *= 2;
// Por const reference (eficiente, without copy):
for (const auto& item : nums) {
std::cout << item << "\n";
}for (auto& x : container) iterates without an index. const auto& avoids copies (essential for strings/objects). auto& allows modifying. More readable than iterators. Prefer it whenever possible.
if with initialization (C++17)
// Variable com escopo limitado ao if:
if (auto it = map.find("key"); it != map.end()) {
std::cout << it->second;
}
// 'it' not exists here
if (int n = std::stoi(input); n > 0) {
process(n);
}
// Also em switch:
switch (auto [ok, val] = try(); ok) {
case true: usar(val); break;
case false: error(); break;
}if (init; condition) declares a variable scoped to the block. It avoids polluting the outer scope. Very used with find() and conversions. It also works in switch. C++17 feature.
Functions
Function with return
int add(int a, int b) {
return a + b;
}
void greeting(const std::string& name) {
std::cout << "Hello, " << name << "!\n";
}
int main() {
int r = add(3, 7); // 10
greeting("Anna"); // "Hello, Anna!"
}Functions have a return type, a name and parameters. void = no return. return returns the value and ends. Parameters are passed by value (copy) by default.
Lambda expressions
// Lambda simple:
auto double = [](int x) { return x * 2; };
double(5); // 10
// With capture by reference:
int total = 0;
auto accumulate = [&total](int x) { total += x; };
accumulate(10);
accumulate(20); // total = 30
// As comparator:
std::sort(v.begin(), v.end(),
[](int a, int b) { return a > b; });
// Capture by value:
auto copy = [x]() { return x; };Lambda = inline anonymous function. [] is the capture: [&] by reference, [=] by value, [x] a specific variable. Ideal for callbacks and comparators in STL somethingrithms.
Functions with auto return
// Return inferido (C++14):
auto divide(double a, double b) {
return a / b; // returns double
}
// Trailing return type:
auto add(int a, int b) -> int {
return a + b;
}
// Useful com types complexos:
auto getMap() {
return std::map<std::string, std::vector<int>>{};
}
// decltype(auto) preserva reference:
decltype(auto) first(std::vector<int>& v) {
return v[0]; // returns int&
}auto the a return lets the compiler infer. -> type (trailing) is explicit. decltype(auto) preserves references and cv-qualifiers. Useful for long types and templates.
Passing by reference
// Modifica o original:
void increment(int& x) {
x++;
}
// Only reading (without copy):
void print(const std::string& s) {
std::cout << s << "\n";
// s = "error"; // ERROR: is const
}
int n = 5;
increment(n); // n = 6& passes by reference (no copy, modifies the original). const & is read-only but avoids a copy — always use it for strings, vectors and objects. Essential for performance.
inline and constexpr
// inline: hint to avoid overhead de call
inline int abs(int x) {
return x < 0 ? -x : x;
}
// constexpr: evaluated at compile time
constexpr int square(int n) {
return n * n;
}
constexpr int ARR[5] = {
square(1), square(2), square(3),
square(4), square(5)
};
// ARR = {1, 4, 9, 16, 25} — everything em compilationinline suggests to the compiler to expand the function (avoids call overhead). constexpr guarantees compile-time evaluation if the args are constant. Arrays and templates benefit from constexpr.
Variadic arguments (C-style)
#include <cstdarg>
double average(int n, ...) {
va_list args;
va_start(args, n);
double sum = 0;
for (int i = 0; i < n; i++) {
sum += va_arg(args, double);
}
va_end(args);
return sum / n;
}
average(3, 10.0, 20.0, 30.0); // 20.0va_list/va_arg allow a variable number of arguments (C style). Unsafe (no type-check). In modern C++, prefer variadic templates or std::initializer_list.
Default parameters
void connect(const std::string& host = "localhost",
int port = 3306,
bool ssl = false) {
std::cout << host << ":" << port
<< (ssl ? " (SSL)" : "") << "\n";
}
connect(); // localhost:3306
connect("db.local"); // db.local:3306
connect("db.local", 5432); // db.local:5432
connect("db.local", 5432, true);Parameters with = value are optional in the call. They must be at the end of the list. Calls can omit the last arguments. It avoids unnecessary overloads.
noexcept and specifications
// Promises not throw exceptions:
void swap(int& a, int& b) noexcept {
int tmp = a;
a = b;
b = tmp;
}
// Condicional:
template <typename T>
void move(T& a, T& b) noexcept(noexcept(T(std::move(a)))) {
T tmp = std::move(a);
a = std::move(b);
b = std::move(tmp);
}
// Check:
static_assert(noexcept(swap(a, b)));noexcept promises that the function does not throw exceptions. It allows optimizations (move instead of copy in vector). If violated, it calls std::terminate. Essential in move constructors and swap.
Function overloading
int sum(int a, int b) { return a + b; }
double sum(double a, double b) { return a + b; }
std::string sum(const std::string& a, const std::string& b) {
return a + b;
}
sum(3, 5); // int → 8
sum(3.1, 2.9); // double → 6.0
sum("Hello", "!"); // string → "Hello!"Overloading = same name, different parameters. The compiler chooses by the type of the arguments. It cannot differ only in the return. Widely used in the STL (e.g. std::abs for int/double).
Function pointers
int sum(int a, int b) { return a + b; }
int mult(int a, int b) { return a * b; }
// Pointer de func:
int (*op)(int, int) = ∑
op(3, 4); // 7
op = &mult;
op(3, 4); // 12
// Com std::function (flexible):
#include <functional>
std::function<int(int,int)> f = sum;
f = [](int a, int b) { return a - b; };Function pointers store the addresses of functions. Syntax: type (*name)(params). std::function is more flexible (accepts lambdas, binds). Used for callbacks and strategies.
Classes and Objects
Class with constructor
class Person {
private:
std::string name;
int age;
public:
// Constructor com list de initialization:
Person(std::string n, int i)
: name(std::move(n)), age(i) {}
// Getters:
const std::string& getName() const { return name; }
int getAge() const { return age; }
};
Person p("Anna", 30);The initialization list (: name(n), age(i)) is more efficient than assigning in the body. std::move avoids copying strings. const on the method promises not to modify the object.
Operator overloading
class Point {
public:
double x, y;
Point operator+(const Point& o) const {
return {x + o.x, y + o.y};
}
bool operator==(const Point& o) const {
return x == o.x && y == o.y;
}
friend std::ostream& operator<<(std::ostream& the, const Point& p) {
return the << "(" << p.x << ", " << p.y << ")";
}
};Overload operator+, ==, << for natural usage. const at the end = does not modify. friend accesses privates (for <<). Allows p1 + p2 and cout << p.
Copy and move constructor
class Buffer {
std::unique_ptr<int[]> data;
size_t tam;
public:
// Move constructor:
Buffer(Buffer&& o) noexcept
: data(std::move(o.data)), tam(o.tam) {
o.tam = 0;
}
// Copy constructor:
Buffer(const Buffer& o) : tam(o.tam),
data(std::make_unique<int[]>(o.tam)) {
std::copy(o.data.get(), o.data.get() + tam, data.get());
}
};The move constructor (&&) transfers resources without copying — fast. The copy constructor (const &) duplicates data. noexcept on the move is essential for vector to use move on resize.
Inheritance
class Animal {
protected:
std::string name;
public:
Animal(std::string n) : name(std::move(n)) {}
virtual void speak() const = 0; // puro virtual
virtual ~Animal() = default;
};
class Dog : public Animal {
public:
Dog(std::string n) : Animal(std::move(n)) {}
void speak() const override {
std::cout << name << ": Au!\n";
}
};public inherits the interface. virtual allows overriding. = 0 makes it pure virtual (abstract class). override confirms it overrides a base method. A virtual destructor is mandatory in base classes.
struct vs class
// struct: membros public por default
struct Point {
double x, y; // public
};
// class: membros privados por default
class Account {
double balance; // private
public:
void deposit(double v) { balance += v; }
};
// Convention:
// struct → data simple (POD, DTOs)
// class → logic + encapsulationThe only difference: struct has public members by default, class private. Convention: struct for simple data (no invariants), class for objects with logic. Technically interchangeable.
Interfaces (abstract class)
class ISerializable {
public:
virtual ~ISerializable() = default;
virtual std::string toJSON() const = 0;
virtual void fromJSON(const std::string& json) = 0;
};
class User : public ISerializable {
public:
std::string toJSON() const override {
return R"({"name": ")" + name + R"("})";
}
void fromJSON(const std::string& json) override {
// parse...
}
};An abstract class = interface: only pure virtual methods (= 0). It cannot be instantiated. Derived classes implement everything. A virtual destructor is mandatory. Equivalent to Java/C# interfaces.
Polymorphism
std::vector<std::unique_ptr<Animal>> animals;
animals.push_back(std::make_unique<Dog>("Rex"));
animals.push_back(std::make_unique<Cat>("Miau"));
// Dispatch dynamic:
for (const auto& a : animals) {
a->speak(); // chama o override correto
}
// Downcast safe:
if (auto* dog = dynamic_cast<Dog*>(animals[0].get())) {
dog->wagTail();
}Polymorphism = calling the correct method via a base pointer. It requires virtual. unique_ptr manages memory automatically. dynamic_cast does a safe downcast (returns nullptr on failure).
static members
class Counter {
static int total; // shared by all
public:
Counter() { total++; }
~Counter() { total--; }
static int getTotal() { return total; }
};
// Define outside the class:
int Counter::total = 0;
Counter a, b, c;
Counter::getTotal(); // 3static members belong to the class, not to the object. Shared by all instances. Access via Class::member. Must be defined outside the class. Ideal for counters and constants.
Rule of Five
class Recurso {
int* data;
public:
Recurso(int n) : data(new int[n]) {}
~Recurso() { delete[] data; } // destructor
Recurso(const Recurso& o); // copy
Recurso& operator=(const Recurso& o); // assignment copy
Recurso(Recurso&& o) noexcept; // move
Recurso& operator=(Recurso&& o) noexcept; // assignment move
};
// Ou: Rule of Zero (usa smart pointers!)If you define one, you probably need all 5: destructor, copy ctor, copy assign, move ctor, move assign. Rule of Zero: use unique_ptr/shared_ptr and define none.
Friend and encapsulation
class Secret {
int code = 42;
friend class Hacker; // class amiga
friend void reveal(Secret&); // func amiga
};
class Hacker {
public:
void aceder(Secret& s) {
std::cout << s.code; // OK: is friend
}
};
void reveal(Secret& s) {
std::cout << s.code; // OK: is friend
}friend grants access to private members. It breaks encapsulation — use it sparingly. Useful for << operators, unit tests and tightly-coupled classes. It is neither transitive nor inherited.
STL Containers
std::vector
#include <vector>
std::vector<int> v = {1, 2, 3};
v.push_back(4); // adds at the end
v.size(); // 4
v[0]; // 1 (without bounds check)
v.at(0); // 1 (with bounds check)
v.front(); v.back(); // first / last
v.pop_back(); // remove last
v.erase(v.begin() + 1); // remove index 1
v.clear(); // remove all
v.empty(); // truevector is the most used container — a contiguous dynamic array. push_back adds in amortized O(1). at() checks bounds (throws an exception). erase removes by iterator. Reserve with reserve() for performance.
std::list
#include <list>
std::list<int> l = {3, 1, 4};
l.push_front(0);
l.push_back(5);
l.sort(); // sorts (not usa std::sort)
l.unique(); // remove duplicates consecutivos
l.reverse();
// Insertion/removal O(1) em any position
// (com iterator):
auto it = std::next(l.begin(), 2);
l.insert(it, 99);
l.erase(it);list is a doubly linked list. Insertion/removal is O(1) with an iterator. No index access. sort() and splice() are its own methods. Use it for frequent insertions in the middle. Rarely needed.
Iterators
std::vector<int> v = {10, 20, 30};
// Iterator classic:
for (auto it = v.begin(); it != v.end(); ++it) {
std::cout << *it << " ";
}
// reverse_iterator:
for (auto it = v.rbegin(); it != v.rend(); ++it) {
std::cout << *it << " "; // 30 20 10
}
// Advance N positions:
auto it = v.begin();
std::advance(it, 2); // points to 30
auto dist = std::distance(v.begin(), it); // 2Iterators point to elements. begin()/end() delimit the range. * dereferences. rbegin/rend for reverse order. std::advance moves N positions. They are the base of all STL somethingrithms.
std::map and unordered_map
#include <map>
#include <unordered_map>
// map: sorted by key (tree)
std::map<std::string, int> grades;
grades["ana"] = 95;
grades["joao"] = 88;
// unordered_map: hash (faster)
std::unordered_map<std::string, int> cache;
cache["key"] = 42;
// Safe access:
if (auto it = grades.find("ana"); it != grades.end()) {
std::cout << it->second; // 95
}map keeps keys sorted (O(log n)). unordered_map uses a hash (average O(1)). operator[] creates if it does not exist. find + end() for safe access. at() throws an exception if it does not exist.
std::stack and std::queue
#include <stack> #include <queue> // Stack (LIFO): std::stack<int> stack; stack.push(1); stack.push(2); stack.push(3); stack.top(); // 3 stack.pop(); // remove 3 stack.size(); // 2 // Queue (FIFO): std::queue<int> queue; queue.push(1); queue.push(2); queue.push(3); queue.front(); // 1 queue.back(); // 3 queue.pop(); // remove 1
stack = LIFO (last in, first out). queue = FIFO (first in, first out). They are adapters (they use deque underneath). No iterators. Ideal for DFS/BFS and undo/redo.
std::array and std::span
#include <array>
#include <span> // C++20
std::array<int, 5> arr = {1, 2, 3, 4, 5};
arr.size(); // 5 (compile-time)
arr.fill(0); // all a 0
// span: view about sequence contiguous (C++20)
void process(std::span<int> data) {
for (int x : data) std::cout << x;
}
process(arr); // de array
std::vector<int> v = {1,2};
process(v); // de vector
process({arr.data(), 3}); // sub-rangestd::array is fixed in size (compile-time), no heap. std::span (C++20) is a lightweight view over contiguous data (array, vector, C-array). No ownership. It replaces the pointer+size pair.
std::set and unordered_set
#include <set>
#include <unordered_set>
std::set<int> s = {3, 1, 4, 1, 5};
// s = {1, 3, 4, 5} — unique e sorted
s.insert(9);
s.count(3); // 1 (exists)
s.erase(1);
s.size(); // 4
// unordered_set: hash, without order
std::unordered_set<std::string> tags;
tags.insert("cpp");
tags.contains("cpp"); // true (C++20)set stores unique, sorted values. unordered_set is faster (hash). insert ignores duplicates. contains (C++20) checks presence. Ideal for checking existence and removing duplicates.
std::priority_queue
#include <queue>
// Max-heap (largest no top):
std::priority_queue<int> pq;
pq.push(3); pq.push(1); pq.push(4);
pq.top(); // 4 (largest)
pq.pop(); // remove 4
// Min-heap:
std::priority_queue<int, std::vector<int>,
std::greater<int>> min_pq;
min_pq.push(3); min_pq.push(1);
min_pq.top(); // 1 (smallest)priority_queue is a heap — the top is always the largest (max-heap). For a min-heap use std::greater. push/pop in O(log n). Ideal for scheduling, Dijkstra, top-K elements.
std::deque
#include <deque>
std::deque<int> d = {2, 3, 4};
d.push_front(1); // {1, 2, 3, 4}
d.push_back(5); // {1, 2, 3, 4, 5}
d.pop_front(); // remove 1
d.pop_back(); // remove 5
d[2]; // access by index
// vs vector:
// + push_front O(1)
// - without memory contiguous
// - slower iteratorsdeque = double-ended queue. push_front and push_back in O(1). No contiguous memory (blocks). Use it when you need to insert/remove at both ends. It is the base of stack and queue.
std::pair and std::tuple
#include <utility>
#include <tuple>
auto p = std::make_pair("Anna", 30);
p.first; // "Anna"
p.second; // 30
// Structured binding (C++17):
auto [name, age] = p;
// Tuple (more than 2):
auto t = std::make_tuple(1, "ok", 3.14);
auto [a, b, c] = t;
std::get<0>(t); // 1
// tie (assign to existing):
int x; std::string s;
std::tie(x, s) = std::make_tuple(42, "hi");pair groups 2 values. tuple groups N. structured binding destructures elegantly. std::get<N> accesses by index. Multiple function returns use pair/tuple.
Pointers and Memory
Basic pointers
int x = 42; int* p = &x; // p stores address de x std::cout << *p; // 42 (dereferencing) *p = 100; // modifica x std::cout << x; // 100 int* null = nullptr; // pointer null (C++11) // Pointer to pointer: int** pp = &p; **pp; // 42 → 100
& gets the address. * dereferences (accesses the value). nullptr is the modern null pointer (replaces NULL). Always initialize pointers. Check before dereferencing.
std::shared_ptr
#include <memory>
auto sp1 = std::make_shared<Resource>();
{
auto sp2 = sp1; // shares (ref count = 2)
auto sp3 = sp1; // ref count = 3
}
// sp2, sp3 destroyed → ref count = 1
sp1.use_count(); // 1
sp1.reset(); // releases (ref count = 0)
// Cost: atomic ref counting (slower than unique)
// Use when several owners need the same objectshared_ptr = shared ownership with reference counting. It frees when the count = 0. It is copyable (increments the count). It has atomic ops overhead. Use it when multiple owners share the same resource. Prefer unique_ptr if possible.
Dynamic arrays and C-arrays
// C-array (avoid):
int arr[5] = {1, 2, 3, 4, 5};
int* dyn = new int[n];
delete[] dyn;
// Moderno (prefer):
std::vector<int> v(n); // dynamic
std::array<int, 5> a = {}; // fixo, stack
auto up = std::make_unique<int[]>(n); // heap, RAII
// Pointer + size → span (C++20):
void process(std::span<int> data);
process(v);
process({arr, 5});Avoid C-arrays and new[]. vector for dynamic size. std::array for fixed. make_unique<T[]> if you need heap with ownership. std::span (C++20) the a universal parameter.
References
int x = 10;
int& ref = x; // alias to x
ref = 20; // x now is 20
// Not can be null nor reassigned:
// int& r; // ERROR: must initialize
// &ref = &y; // impossible
// Em parameters:
void swap(int& a, int& b) {
std::swap(a, b);
}
// Reference constant:
const int& cr = x; // only readingA reference = alias for an existing variable. It must be initialized. It cannot be null nor reassigned. const & prevents modification. Prefer references over pointers when null is not an option.
std::weak_ptr
#include <memory>
std::weak_ptr<Recurso> weak;
{
auto strong = std::make_shared<Recurso>();
weak = strong;
if (auto sp = weak.lock()) {
sp->usar(); // OK: still exists
}
}
// strong destroyed
if (weak.expired()) {
// object already not exists
}
auto sp = weak.lock(); // nullptrweak_ptr observes without owning (it does not increment the ref count). It resolves reference cycles. lock() tries to obtain a shared_ptr (nullptr if expired). expired() checks. Essential for caches and observers.
lock_guard and resources
#include <mutex>
std::mutex mtx;
int partilhado = 0;
void increment() {
std::lock_guard<std::mutex> lock(mtx);
partilhado++;
// unlock automatic ao exit do scope
}
// C++17 (deduction de type):
void other() {
std::lock_guard lock(mtx);
partilhado += 10;
}
// scoped_lock (multiple mutexes):
void multi() {
std::scoped_lock lock(mtx1, mtx2);
}lock_guard applies RAII to mutexes: lock in the constructor, unlock in the destructor. Impossible to forget the unlock. scoped_lock (C++17) locks multiple mutexes without deadlock. The standard pattern for thread safety.
new and delete (avoid)
// Allocation manual:
int* p = new int(42);
delete p; // release
p = nullptr; // avoid dangling
// Array:
int* arr = new int[100];
delete[] arr; // delete[] to arrays!
// Object:
auto* obj = new Person("Anna");
delete obj;
// ⚠️ Risks: memory leaks, double free,
// dangling pointers. PREFERIR smart pointers!new allocates on the heap, delete frees. delete[] for arrays. If you forget the delete = memory leak. If you delete twice = crash. In modern C++, always use unique_ptr/shared_ptr.
RAII
// RAII: Resource Acquisition Is Initialization
class File {
FILE* f;
public:
File(const char* path) {
f = fopen(path, "r");
if (!f) throw std::runtime_error("error");
}
~File() {
if (f) fclose(f); // releases in destructor
}
// Not copyable:
File(const File&) = delete;
};
// Use:
{
File f("data.txt");
// usar f...
} // fclose automatic here!RAII = acquire the resource in the constructor, release it in the destructor. It guarantees cleanup even with exceptions. It is the base of unique_ptr, lock_guard, fstream. The most important pattern in C++.
std::unique_ptr
#include <memory>
// Ownership exclusiva (not copyable):
auto p = std::make_unique<Person>("Anna", 30);
p->getName(); // "Anna"
// Transferir ownership (move):
auto p2 = std::move(p);
// p now is nullptr!
// Array:
auto arr = std::make_unique<int[]>(100);
arr[0] = 42;
// Liberta automatically ao exit do scope
// Sem need de delete!unique_ptr = exclusive ownership. It cannot be copied, only moved (std::move). It frees memory automatically (RAII). make_unique (C++14) creates it safely. It replaces new/delete in most cases.
Move semantics
#include <utility>
std::vector<int> create() {
return {1, 2, 3, 4, 5}; // move (RVO)
}
auto v1 = create();
auto v2 = std::move(v1); // transfere buffer
// v1 now is empty (moved-from)
// Rvalue reference:
void process(std::string&& s) {
data_ = std::move(s); // steals resources
}
process(std::string("temporary")); // OK
// std::string name = "x";
// process(std::move(name)); // move explicitstd::move converts to an rvalue (allows stealing resources). && is an rvalue reference. It avoids expensive copies. After a move, the object is in a valid but unspecified state. RVO optimizes returns automatically.
Templates
Template function
template <typename T>
T largest(T a, T b) {
return (a > b) ? a : b;
}
largest(3, 7); // int → 7
largest(3.14, 2.71); // double → 3.14
largest<std::string>("a", "b"); // explicit
// Multiple types:
template <typename T, typename U>
auto sum(T a, U b) {
return a + b;
}template <typename T> creates a generic function. The compiler generates code for each type used. T is substituted at compile time. Zero overhead vs manual code. The base of generic programming in C++.
Concepts (C++20)
#include <concepts>
// Usar concept existing:
template <std::integral T>
T sum(T a, T b) { return a + b; }
template <std::floating_point T>
T root(T x) { return std::sqrt(x); }
// Define custom concept:
template <typename T>
concept Printable = requires(T x) {
{ std::cout << x } -> std::same_as<std::ostream&>;
};
void print(const Printable auto& x) {
std::cout << x;
}Concepts (C++20) constrain templates with clear requirements. Readable compile errors. std::integral, std::floating_point are built-in. requires defines custom ones. It replaces SFINAE.
CRTP
// Curiously Recurring Template Pattern
template <typename Derived>
class Base {
public:
void interface() {
// "Polymorphism" static (without virtual):
static_cast<Derived*>(this)->implementation();
}
};
class Concrete : public Base<Concrete> {
public:
void implementation() {
std::cout << "Done!\n";
}
};
Concrete c;
c.interface(); // "Done!" — resolvido em compilationCRTP = a class derives from a template of itself. It enables static polymorphism (no vtable, faster). Used for mix-ins, counters and compile-time interfaces. An advanced but powerful pattern.
Template class
template <typename T>
class Box {
T value;
public:
Box(T v) : value(std::move(v)) {}
T get() const { return value; }
void set(T v) { value = std::move(v); }
};
Box<int> c(42);
Box<std::string> s("hello");
// C++17: CTAD (deduction automatic):
Box c2(42); // deduz Box<int>
Box s2("hello"s); // deduz Box<std::string>Template classes parametrize types. Instantiate with Box<int>. CTAD (C++17) deduces the type from the constructor. The STL is entirely template-based: vector<T>, map<K,V>.
Variadic templates
// Number variable de arguments:
template <typename... Args>
void print(Args... args) {
(std::cout << ... << args) << "\n";
}
print(1, " + ", 2, " = ", 3);
// Fold expressions (C++17):
template <typename... Args>
auto sum(Args... args) {
return (args + ...); // fold right
}
sum(1, 2, 3, 4); // 10
// sizeof... account:
template <typename... Ts>
constexpr size_t count() { return sizeof...(Ts); }Variadic templates accept N types. Args... is a parameter pack. A fold expression ((args + ...)) expands elegantly. sizeof... counts the elements. The base of std::tuple, make_unique, and a safe printf.
if constexpr
template <typename T>
std::string toStr(const T& val) {
if constexpr (std::is_same_v<T, std::string>) {
return val; // already is string
} else if constexpr (std::is_arithmetic_v<T>) {
return std::to_string(val);
} else {
return val.toString(); // tem method
}
}
toStr(42); // "42"
toStr(std::string("oi")); // "oi"
// Non-compiled branches are discarded!if constexpr (C++17) evaluates at compile time. False branches are discarded (they do not need to compile). It removes the need for SFINAE/tag dispatch. Ideal for generic code with per-type branches.
Specialization
// Template generic:
template <typename T>
struct TypeInfo {
static std::string name() { return "unknown"; }
};
// Full specialization:
template <>
struct TypeInfo<int> {
static std::string name() { return "integer"; }
};
template <>
struct TypeInfo<double> {
static std::string name() { return "real"; }
};
TypeInfo<int>::name(); // "integer"
TypeInfo<char>::name(); // "unknown"Full specialization (template <>) replaces the template for a specific type. The compiler chooses the most specific version. Used in type traits and metaprogramming.
Type traits
#include <type_traits>
// Check at compile time:
static_assert(std::is_integral_v<int>);
static_assert(std::is_floating_point_v<double>);
static_assert(std::is_same_v<int, int32_t>);
// Transform types:
using T1 = std::remove_const_t<const int>; // int
using T2 = std::remove_pointer_t<int*>; // int
using T3 = std::add_lvalue_reference_t<int>; // int&
// Condicional:
template <typename T>
using Storage = std::conditional_t<
sizeof(T) <= 8, T, const T&>;Type traits query and transform types at compile time. _v = boolean value, _t = resulting type. static_assert fails compilation if false. Essential for metaprogramming and SFINAE.
Partial specialization
// Generic:
template <typename T>
struct Container { using type = T; };
// Partial specialization to pointers:
template <typename T>
struct Container<T*> {
using type = T; // remove o *
};
// To vectors:
template <typename T>
struct Container<std::vector<T>> {
using type = T; // element type
};
Container<int*>::type; // int
Container<std::vector<double>>::type; // doublePartial specialization specializes a pattern (e.g. all pointers). Only for classes/structs (not functions). The T* pattern captures any pointer. The base of type traits like remove_pointer.
SFINAE
// enable_if: only compila se condition for true
template <typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
half(T x) { return x / 2; }
// C++20 (prefer concepts):
template <std::integral T>
T half(T x) { return x / 2; }
// if constexpr (C++17):
template <typename T>
auto process(T x) {
if constexpr (std::is_integral_v<T>)
return x / 2;
else
return x;
}SFINAE = Substitution Failure Is Not An Error. It removes invalid overloads silently. enable_if is the classic form. C++17/20 offer better alternatives: if constexpr and concepts.
Modern C++
auto and decltype
auto x = 42; // int
auto s = std::string("hello"); // std::string
auto& ref = x; // int&
const auto& cr = x; // const int&
// decltype: type de uma expression
decltype(x) y = 10; // int
decltype(x + 1.0) z; // double
// decltype(auto) (C++14):
auto getVec() { return std::vector<int>{1,2,3}; }
decltype(auto) first(std::vector<int>& v) {
return v[0]; // int& (preserva reference)
}auto infers the type (avoids repetition). decltype gets the type of an expression. decltype(auto) preserves references and cv-qualifiers. Use auto for iterators and long types. Don't overuse it for obvious types.
std::string_view (C++17)
#include <string_view>
// Lightweight view (without copy):
void process(std::string_view sv) {
sv.substr(0, 5);
sv.size();
sv.starts_with("Hello"); // C++20
}
process("Hello World"); // from literal
std::string s = "test";
process(s); // from string
process(std::string_view(s.data(), 3)); // sub
// ⚠️ Does not own the data!
// Do not keep it if the source can diestring_view is a read-only view over existing chars (no copy). It accepts literals, strings, substrings. Zero allocation. Careful: it is not an owner — if the source dies, it becomes dangling. Ideal for function parameters.
Coroutines (C++20)
#include <coroutine>
#include <generator> // C++23
// Generator simple (C++23):
std::generator<int> fibonacci() {
int a = 0, b = 1;
while (true) {
co_yield a;
auto tmp = a;
a = b;
b = tmp + b;
}
}
for (int x : fibonacci()) {
if (x > 100) break;
std::cout << x << " ";
}
// 0 1 1 2 3 5 8 13 21 34 55 89Coroutines (C++20) are functions that can suspend (co_yield, co_await, co_return). std::generator (C++23) simplifies them. Lazy, efficient for infinite sequences and asynchronous I/O.
Structured bindings (C++17)
// De pair:
auto [name, age] = std::make_pair("Anna", 30);
// De tuple:
auto [x, y, z] = std::make_tuple(1.0, 2.0, 3.0);
// De struct:
struct Point { double x, y; };
auto [px, py] = Point{3.0, 4.0};
// Em range-for:
std::map<std::string, int> m = {{"a", 1}, {"b", 2}};
for (const auto& [key, val] : m) {
std::cout << key << " = " << val << "\n";
}Structured bindings destructure pairs, tuples, arrays and structs. auto [a, b] = ... creates local variables. It works in range-for for maps. More readable than .first/.second.
Ranges (C++20)
#include <ranges>
namespace views = std::views;
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8};
auto result = v
| views::filter([](int n) { return n % 2 == 0; })
| views::transform([](int n) { return n * n; })
| views::take(3);
for (int x : result) {
std::cout << x << " "; // 4 16 36
}
// Lazy evaluation: nothing is calculado up to iterateRanges (C++20) compose operations into pipelines with |. Lazy evaluation (only computes when iterating). filter, transform, take are views. It replaces loops + temporary vectors. Inspired by Python/Rust.
Designated initializers (C++20)
struct Config {
int width = 800;
int height = 600;
bool fullscreen = false;
std::string title = "App";
};
// C++20:
Config cfg{
.width = 1920,
.height = 1080,
.fullscreen = true,
// .title usa default "App"
};
// Order must match the declaration order
// Fields omitidos usam default
Config minimal{.title = "Game"};Designated initializers (C++20) name the fields at initialization. The order must follow the declaration. Omitted fields use defaults. More readable than positional initialization. It works with structs and aggregate classes.
std::optional (C++17)
#include <optional>
std::optional<int> find(int id) {
if (id > 0) return id * 10;
return std::nullopt;
}
auto result = find(5);
if (result.has_value()) {
std::cout << *result; // 50
}
// Value_or (fallback):
int val = find(-1).value_or(0); // 0
// Em parameters (optional explicit):
void config(std::optional<int> timeout = std::nullopt);std::optional = a value that may not exist (without sentinels like -1). has_value() checks, * accesses. value_or gives a fallback. nullopt = empty. It replaces null pointers for optionals.
std::chrono
#include <chrono> using namespace std::chrono; // Measure time: auto start = steady_clock::now(); doSomething(); auto end = steady_clock::now(); auto ms = duration_cast<milliseconds>(end - start); std::cout << ms.count() << " ms\n"; // Literais (C++14): using namespace std::chrono_literals; auto timeout = 5s; auto delay = 100ms; std::this_thread::sleep_for(delay); // Timestamp: auto now = system_clock::now(); auto epoch = now.time_since_epoch();
std::chrono handles time with type safety. steady_clock for measurements (monotonic). duration_cast converts units. Literals 5s, 100ms (C++14). It replaces clock() and C APIs.
std::variant (C++17)
#include <variant>
std::variant<int, double, std::string> v;
v = 42;
v = 3.14;
v = "hello";
std::get<std::string>(v); // "hello"
std::get<2>(v); // por index
v.index(); // 2 (type active)
std::holds_alternative<int>(v); // false
// std::visit (pattern matching):
std::visit([](auto&& val) {
std::cout << val << "\n";
}, v);std::variant = a union with type safety. It holds ONE of N types. std::get accesses it (throws an exception if the type is wrong). std::visit applies a visitor. A safe replacement for C unions. No heap allocation.
std::filesystem (C++17)
#include <filesystem>
namespace fs = std::filesystem;
fs::path p = "/home/user/file.txt";
p.filename(); // "file.txt"
p.extension(); // ".txt"
p.parent_path(); // "/home/user"
fs::exists(p);
fs::file_size(p);
fs::create_directories("a/b/c");
fs::copy("orig.txt", "dest.txt");
fs::remove("temp.txt");
// Iterate directory:
for (const auto& entry : fs::directory_iterator(".")) {
std::cout << entry.path() << "\n";
}std::filesystem (C++17) manipulates files and folders portably. path composes/decomposes paths. exists, copy, remove, create_directories. It replaces OS APIs.
Compilation and Tips
Compiling with g++ / clang++
# Basic: g++ main.cpp -o app # Com standard e warnings: g++ -std=c++20 -Wall -Wextra -O2 main.cpp -o app # Clang: clang++ -std=c++20 -Wall main.cpp -o app # Multiple files: g++ -std=c++20 main.cpp utils.cpp -o app # Execute: ./app
g++ and clang++ are the main compilers. -std=c++20 sets the standard. -Wall -Wextra enables warnings. -O2 optimizes. Always compile with warnings enabled.
Preprocessor
#define MAX_SIZE 1024
#define SQUARE(x) ((x) * (x))
#ifdef DEBUG
#define LOG(msg) std::cout << "[DBG] " << msg << "\n"
#else
#define LOG(msg)
#endif
#if __cplusplus >= 202002L
// code C++20
#endif
#pragma once // header guard
#undef MAX_SIZE // remove macroThe preprocessor runs before compilation. #define creates macros (textual substitution). #ifdef conditional compilation. #pragma once prevents double inclusion. Prefer constexpr over macros.
Modern best practices
// ✅ DO:
// • Smart pointers (unique_ptr, shared_ptr)
// • const on everything that does not change
// • const reference to parameters
// • auto for long types/iterators
// • Range-for instead of indexes
// • [[nodiscard]] on important returns
[[nodiscard]] int calculate() { return 42; }
// ❌ AVOID:
// • new/delete manual
// • using namespace std in headers
// • C-arrays and C-strings
// • Macros (prefer constexpr)
// • Raw pointers the ownersGolden rules: smart pointers instead of new/delete, const by default, const & for parameters, range-for to iterate. [[nodiscard]] warns if the return is ignored. Follow the C++ Core Guidelines.
Basic CMake
cmake_minimum_required(VERSION 3.20)
project(MeuProjeto LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(app
src/main.cpp
src/utils.cpp
)
target_include_directories(app PRIVATE include)
# Build:
# mkdir build && cd build
# cmake .. && cmake --build .CMake is the de facto standard build system. add_executable defines the target. target_include_directories adds headers. Out-of-source build in build/. Cross-platform (Linux, Win, Mac).
Sanitizers
# Address Sanitizer (memory errors): g++ -fsanitize=address -g main.cpp -o app # Undefined Behavior Sanitizer: g++ -fsanitize=undefined -g main.cpp -o app # Thread Sanitizer (data races): g++ -fsanitize=thread -g main.cpp -o app # Memory Sanitizer (uninitialized reads): clang++ -fsanitize=memory -g main.cpp -o app # Combine (exceto thread): g++ -fsanitize=address,undefined -g main.cpp -o app
Sanitizers detect bugs at runtime. address = buffer overflow, use-after-free. undefined = signed overflow, null deref. thread = data races. -g for stack traces. Always use them in tests.
Linking and libraries
# Compile to object file: g++ -c -std=c++20 utils.cpp -o utils.o # Create static library: ar rcs libutils.a utils.o # Create shared library: g++ -shared -fPIC utils.o -o libutils.so # Link: g++ main.cpp -L. -lutils -o app # CMake (automatic): # add_library(utils STATIC src/utils.cpp) # target_link_libraries(app PRIVATE utils)
-c compiles without linking (produces .o). ar creates a static library (.a). -shared -fPIC creates a dynamic one (.so/.dll). -L sets the path, -l links. CMake handles everything automatically.
Headers and includes
// main.h (header):
#pragma once // header guard
#include <string>
#include <vector>
namespace app {
void start(const std::string& name);
std::vector<int> generate(int n);
}
// main.cpp (implementation):
#include "main.h"
#include <iostream>
void app::start(const std::string& name) {
std::cout << "Hello " << name << "\n";
}#pragma once prevents double inclusion. Headers declare, .cpp implement. #include "file.h" for local ones, <file> for system ones. Minimize includes in headers (forward declare when possible).
Debugging with GDB
# Compile com debug info: g++ -g -O0 main.cpp -o app # Start GDB: gdb ./app # Comandos: (gdb) break main # breakpoint (gdb) break utils.cpp:42 # breakpoint em line (gdb) run # execute (gdb) next # step over (gdb) step # step into (gdb) print variable # ver value (gdb) backtrace # call stack (gdb) continue # continue
GDB is the standard debugger. Compile with -g -O0 (no optimization). break sets breakpoints. next/step advance. print inspects variables. backtrace shows the call stack.
Namespaces
namespace app::utils {
int sum(int a, int b) { return a + b; }
}
// Usar:
int r = app::utils::sum(3, 4);
// using (scope limitado):
void func() {
using namespace app::utils;
sum(1, 2); // OK here
}
// Alias:
namespace au = app::utils;
au::sum(5, 6);
// NEVER: using namespace std; in headers!namespace organizes code and avoids collisions. :: accesses members. using namespace imports (avoid it in headers!). An alias (namespace au = ...) simplifies. C++17 allows nested app::utils.
Exceptions
#include <stdexcept>
void process(int x) {
if (x < 0) {
throw std::invalid_argument("x negative");
}
}
try {
process(-1);
} catch (const std::invalid_argument& e) {
std::cerr << "Error: " << e.what() << "\n";
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << "\n";
} catch (...) {
std::cerr << "Error unknown\n";
}throw raises an exception. try/catch catches it. std::exception is the base. what() gives the message. catch(...) catches everything. RAII guarantees cleanup. Some projects forbid exceptions (performance).
STL Algorithms
std::sort
#include <somethingrithm>
std::vector<int> v = {5, 2, 8, 1, 9};
// Order crescente:
std::sort(v.begin(), v.end());
// {1, 2, 5, 8, 9}
// Order decrescente:
std::sort(v.begin(), v.end(), std::greater<int>());
// Comparator custom:
std::sort(v.begin(), v.end(),
[](int a, int b) { return a % 3 < b % 3; });
// Stable (keeps relative order):
std::stable_sort(v.begin(), v.end());std::sort sorts in O(n log n). It accepts a custom comparator (lambda or functor). std::greater reverses. stable_sort keeps the order of equal elements. It only works on containers with random access.
std::for_each
#include <somethingrithm>
std::vector<int> v = {1, 2, 3, 4, 5};
// Apply func to each element:
std::for_each(v.begin(), v.end(),
[](int& x) { x *= 2; });
// v = {2, 4, 6, 8, 10}
// With state (functor):
struct Printer {
int count = 0;
void operator()(int x) {
std::cout << ++count << ": " << x << "\n";
}
};
std::for_each(v.begin(), v.end(), Printer{});std::for_each applies a function/lambda to each element. It can modify with int&. It accepts functors with state. In C++11+, range-for is generally preferred. for_each_n (C++17) for N elements.
remove and unique
#include <somethingrithm>
std::vector<int> v = {1, 2, 2, 3, 2, 4};
// Remove all the 2s (idiom erase-remove):
v.erase(
std::remove(v.begin(), v.end(), 2),
v.end());
// v = {1, 3, 4}
// Remove duplicates (requires sorted):
std::sort(v.begin(), v.end());
v.erase(std::unique(v.begin(), v.end()), v.end());
// C++20 (simpler):
std::erase(v, 2); // remove all the 2s
std::erase_if(v, [](int x) { return x > 3; });The erase-remove idiom: std::remove shifts elements and returns the new end; erase resizes. C++20 simplifies this with std::erase/std::erase_if. unique removes consecutive duplicates.
std::find and search
#include <somethingrithm>
std::vector<int> v = {10, 20, 30, 40};
// Find value:
auto it = std::find(v.begin(), v.end(), 30);
if (it != v.end()) {
std::cout << "Found: " << *it;
}
// Find com predicado:
auto it2 = std::find_if(v.begin(), v.end(),
[](int x) { return x > 25; });
// Count:
int n = std::count(v.begin(), v.end(), 20); // 1
int m = std::count_if(v.begin(), v.end(),
[](int x) { return x > 15; }); // 3std::find searches by value (O(n)). find_if uses a predicate. It returns an iterator (compare with end()). count/count_if count occurrences. On map/set, use the container's .find() (O(log n)).
min, max and clamp
#include <somethingrithm>
int a = std::min(3, 7); // 3
int b = std::max(3, 7); // 7
auto [mn, mx] = std::minmax({5, 2, 8, 1});
// Min/max de range:
std::vector<int> v = {4, 2, 9, 1};
auto smallest = *std::min_element(v.begin(), v.end());
auto largest = *std::max_element(v.begin(), v.end());
// Clamp (limit to range):
int c = std::clamp(15, 0, 10); // 10
int d = std::clamp(-5, 0, 10); // 0std::min/std::max compare 2 values. min_element/max_element return an iterator to the extreme of the range. std::clamp (C++17) limits a value to [min, max].
partition and nth_element
#include <somethingrithm>
std::vector<int> v = {5, 2, 8, 1, 9, 3};
// Partition (even before de odd):
auto it = std::partition(v.begin(), v.end(),
[](int x) { return x % 2 == 0; });
// [2, 8] | [5, 1, 9, 3]
// Stable:
std::stable_partition(v.begin(), v.end(), pred);
// N-th element (O(n) average):
std::nth_element(v.begin(), v.begin() + 2, v.end());
// v[2] is the 3rd smallest (rest not sorted)
// Mediana:
std::nth_element(v.begin(), v.begin() + v.size()/2, v.end());std::partition splits into 2 groups (predicate true/false). nth_element places the Nth element in its correct position in O(n) — ideal for median and top-K without sorting everything.
std::transform
#include <somethingrithm>
std::vector<int> v = {1, 2, 3, 4, 5};
std::vector<int> result(v.size());
// Transform each element:
std::transform(v.begin(), v.end(), result.begin(),
[](int x) { return x * x; });
// result = {1, 4, 9, 16, 25}
// In-place:
std::transform(v.begin(), v.end(), v.begin(),
[](int x) { return x * 2; });
// Two inputs:
std::transform(a.begin(), a.end(), b.begin(),
result.begin(), std::plus<int>());std::transform applies a function to each element and stores the result. It can be in-place (output = input). The two-range version combines elements. Similar to map in functional languages.
binary_search and lower_bound
#include <somethingrithm>
std::vector<int> v = {1, 3, 5, 7, 9, 11};
// MUST be sorted!
// Exists?
bool exists = std::binary_search(v.begin(), v.end(), 7);
// Insertion position:
auto lb = std::lower_bound(v.begin(), v.end(), 6);
// points to 7 (first >= 6)
auto ub = std::upper_bound(v.begin(), v.end(), 7);
// points to 9 (first > 7)
// Range of equals:
auto [lo, hi] = std::equal_range(v.begin(), v.end(), 7);binary_search checks existence in O(log n) (requires sorted). lower_bound = first ≥ value. upper_bound = first > value. equal_range gives both. Essential for efficient searching.
std::accumulate and reduce
#include <numeric>
std::vector<int> v = {1, 2, 3, 4, 5};
// Sum:
int sum = std::accumulate(v.begin(), v.end(), 0);
// 15
// Product:
int prod = std::accumulate(v.begin(), v.end(), 1,
std::multiplies<int>());
// 120
// Com lambda:
auto concat = std::accumulate(v.begin(), v.end(),
std::string(""), [](std::string acc, int x) {
return acc + std::to_string(x) + ",";
});
// "1,2,3,4,5,"std::accumulate reduces a range to a single value. It starts with an initial value. It accepts a custom operation (lambda). std::reduce (C++17) is parallel. Equivalent to functional reduce/fold.
copy, fill and generate
#include <somethingrithm>
std::vector<int> src = {1, 2, 3, 4, 5};
std::vector<int> dst(5);
// Copy:
std::copy(src.begin(), src.end(), dst.begin());
// Copy with condition:
std::copy_if(src.begin(), src.end(),
std::back_inserter(dst),
[](int x) { return x > 3; });
// Fill:
std::fill(dst.begin(), dst.end(), 0);
// Generate:
std::generate(dst.begin(), dst.end(),
[n = 0]() mutable { return n++; });std::copy copies a range. copy_if filters. back_inserter appends at the end. fill fills with a value. generate calls a function for each position. mutable allows modifying the lambda's capture.