Кладовочка

О разном, чтобы было под рукой онлайн

C++ быстрый справочник: шпаргалка конструкций языка (C++11–23)

C++ — компилируемый язык со статической типизацией, ручным управлением памятью и поддержкой нескольких парадигм. Это большой язык: конструкций много, и держать их все в голове не нужно. Нужен справочник, который открывается за секунду: посмотреть синтаксис, скопировать пример, закрыть.

Чем этот справочник отличается от большой шпаргалки. В большой шпаргалке по C++ разобраны причины, история и подводные камни каждого механизма; здесь — только сами конструкции: синтаксис, короткий комментарий, готовый к копированию пример. Это формат «зашёл, нашёл, взял». Если вы уже писали на C++ и вам нужно освежить память — держите эту страницу в отдельной вкладке. Если разбираетесь с нуля — начните с большого разбора, а сюда возвращайтесь как к словарю.

О чём эта статья

  • Что это: компактный справочник конструкций C++ — от первой программы до C++23: препроцессор, типы, операторы, функции, классы и ООП, шаблоны и concepts, управление памятью, контейнеры и алгоритмы STL, ranges, исключения, многопоточность.
  • Для кого: для программистов, знакомых с основами C++; для подготовки к собеседованию и экзамену; как вторая вкладка рядом с компилятором или cppreference.
  • Как пользоваться: идите по оглавлению или ищите Ctrl+F по имени конструкции (unique_ptr, lambda, ranges, move, optional). Каждый блок самодостаточен — читать всё подряд не обязательно.
  • Чего здесь нет: объяснений «почему так» и разбора типичных ошибок — они собраны в большой шпаргалке и Core Guidelines.

Соглашения. Комментарии в коде на русском, после //. Примеры рассчитаны на C++20/23: собирайте с -std=c++20 (или -std=c++23), а для C++11/14/17 отдельные конструкции будут недоступны — это отмечено в тексте.

Компиляция и первая программа

#include <iostream>            // подключает std::cout и std::cin
#include <string>              // std::string
#include <vector>              // std::vector

int main() {                   // точка входа, возвращает int
    std::cout << "Hello\n";    // вывод в stdout, '\n' быстрее std::endl
    return 0;                  // 0 — успех, любое другое — код ошибки
}
# GCC: стандарт, предупреждения, оптимизация, санитайзеры
g++ -std=c++23 -Wall -Wextra -O2 main.cpp -o app
g++ -std=c++20 -g -fsanitize=address,undefined main.cpp -o app   # отладка

# Clang: то же самое, другой компилятор
clang++ -std=c++23 -Wall -Wextra main.cpp -o app

# MSVC (из Developer Command Prompt)
cl /std:c++latest /EHsc /W4 main.cpp

Флаги, которые стоит набирать всегда: -std=c++20/23 (стандарт), -Wall -Wextra (предупреждения), -O2 (оптимизация), -fsanitize=address,undefined (ловля ошибок памяти и UB при разработке).

Препроцессор

#define PI 3.14159                         // макрос-константа (лучше constexpr)
#define MAX(a, b) ((a) > (b) ? (a) : (b))  // макрос: всегда оборачивайте в скобки!
#define STR(x) #x                          // превратить аргумент в строку
#define CAT(a, b) a##b                     // склеить токены

#ifdef DEBUG                               // условная компиляция
    std::cout << "debug\n";
#else
    std::cout << "release\n";
#endif

#if __cplusplus >= 202002L                 // проверить версию стандарта (C++20)
    // ...
#endif

#ifndef MY_HEADER_H                        // include guard — защита от повторного включения
#define MY_HEADER_H
// ...
#endif
#pragma once                               // нестандартно, но поддерживают все компиляторы

#error "требуется C++20"                   // прервать компиляцию с сообщением
#warning "устаревший код"                  // предупреждение компилятора
#include <vector>                          // системный заголовок (ищется в системных путях)
#include "my.hpp"                          // свой заголовок (ищется рядом с файлом)

Типы данных, литералы и размеры

int i = 42;                  // целое, обычно 4 байта
unsigned u = 42u;            // беззнаковое целое
long l = 42L;                // длинное
long long ll = 42LL;         // минимум 64 бита
double d = 3.14;             // двойная точность
float f = 3.14f;             // одинарная точность
char c = 'A';                // один байт
bool b = true;               // логический
auto x = 42;                 // тип выводится компилятором: int
size_t n = 0;                // беззнаковый тип размеров (обычно 8 байт)

const char* s = "text";      // C-строка: указатель на массив символов
std::string str = "text";    // std::string: сама управляет памятью
std::string_view sv = "view"; // невладеющий вид на строку (C++17)

int bin = 0b1010;            // двоичный литерал (C++14)
int hex = 0xFF;              // шестнадцатеричный
int oct = 0755;              // восьмеричный
long big = 1'000'000;        // разделитель разрядов (C++14)
double sci = 1.5e-3;         // экспоненциальная запись
char nl = '\n';              // escape-последовательность
const char* raw = R"(C:\path\no\escapes)"; // raw-строка (C++11)
auto ch8 = u8'A';            // char8_t (C++20)
std::byte byte{0xFF};        // «сырой» байт, не число (C++17)
Тип Типичный размер Примечание
bool 1 байт обычно true/false
char 1 байт знаковость зависит от платформы
int 4 байта обычно ±2,1 млрд
unsigned 4 байта 0 … 4,29 млрд
long long 8 байт минимум 64 бита по стандарту
float 4 байта ~7 значащих цифр
double 8 байт ~15 значащих цифр
size_t 8 байт (64-бит) результат sizeof, индексы
std::int32_t 4 байта фиксированная ширина, <cstdint>

Размеры фундаментальных типов не фиксированы стандартом — используйте std::intN_t из <cstdint>, когда важна точная ширина.

Объявления, инициализация и спецификаторы

int a = 1;                    // копирующая инициализация
int b(2);                     // прямая инициализация
int c{3};                     // списковая (универсальная) — предпочтительна (C++11)
int arr[3] = {1, 2, 3};       // C-массив
int* ptr = &a;                // указатель: хранит адрес
int& ref = a;                 // ссылка: псевдоним, не может быть пустой
int&& rref = 42;              // rvalue-ссылка: на временный объект (C++11)

const int k = 10;             // константа времени выполнения
constexpr int n = 42;         // вычисляется на этапе компиляции (C++11)
consteval int f() { return 1; } // вызывается только на этапе компиляции (C++20)
constinit int g = 0;          // статическая инициализация без динамики (C++20)
volatile int reg;             // запрет оптимизации доступа (для MMIO, не для потоков)

static int counter;           // одна копия на всю программу или на функцию
extern int global;            // определена в другой единице трансляции
thread_local int tls;         // своя копия в каждом потоке (C++11)
inline int shared = 0;        // одна переменная в нескольких TU (C++17)
mutable int cache;            // поле, изменяемое в const-методе

enum class Color { Red, Green, Blue };  // строго типизированный enum (C++11)
enum Flags { Read = 1, Write = 2 };     // обычный enum: неявно приводится к int
using Id = unsigned long;               // псевдоним типа (C++11)
using Callback = int(*)(int);           // псевдоним указателя на функцию

struct Point { int x, y; };             // агрегат: публичные поля
Point p1{1, 2};                         // агрегатная инициализация

Операторы и выражения

a + b; a - b; a * b; a / b; a % b;      // арифметика (целочисленное деление отбрасывает дробь)
i++; ++i; i--; --i;                      // инкремент/декремент (префиксный эффективнее)
a == b; a != b; a < b; a <= b; a > b;   // сравнение
a && b; a || b; !a;                      // логика с коротким замыканием
a & b; a | b; a ^ b; ~a;                 // битовые операции
a << n; a >> n;                          // сдвиги
x = y; x += y; x -= y; x *= y; x /= y;  // присваивания
condition ? a : b;                       // тернарный оператор
static_cast<int>(d);                     // проверяемое приведение
dynamic_cast<Derived*>(basePtr);         // RTTI-приведение, nullptr при неудаче
const_cast<int*>(constPtr);              // снять const (только если объект не правда const)
reinterpret_cast<char*>(&x);             // переинтерпретация битов (опасно)
sizeof(T); alignof(T); typeid(x);        // размер, выравнивание, тип (RTTI)
&x; *ptr; obj.field; ptr->field;         // адрес, разыменование, доступ к полю

Приоритет (высший → низший): :: → постфиксные ++ -- () [] . -> → префиксные ++ -- ! ~ * & → * / % → + - → << >> → < <= > >= → == != → & → ^ → | → && → || → ?: → = += … → ,. Приоритет не задаёт порядок вычисления: порядок вычисления операндов во многих выражениях не определён.

Ветвления и циклы

if (cond) { }
else if (cond2) { }
else { }

switch (value) {           // value — целое или enum (не строка!)
    case 0: /* ... */ break;
    case 1:
    case 2: /* ... */ break;   // проваливание (fallthrough) — обычно ошибка
    default: break;
}

for (int i = 0; i < n; ++i) { }        // классический for
for (auto& e : container) { }          // range-for по элементам (C++11)
for (auto&& e : container) { }         // forwarding-ссылка, не копирует

while (cond) { }                       // с предусловием
do { } while (cond);                   // с постусловием (выполнится хотя бы раз)

break;       // выйти из цикла/switch
continue;    // перейти к следующей итерации
return x;    // вернуться из функции
goto label;  // безусловный переход (избегайте)
label:

Функции и лямбды

int add(int a, int b) { return a + b; }        // определение
int mul(int a, int b = 2);                      // объявление с аргументом по умолчанию
int add(double a, double b);                    // перегрузка: та же имя, другие типы

inline int sq(int x) { return x * x; }          // совет компилятору встроить тело
constexpr int cube(int x) { return x * x * x; } // может считаться на этапе компиляции
[[nodiscard]] int mustUse();                    // предупреждение, если результат отброшен
[[maybe_unused]] int unused;                    // подавить предупреждение о неиспользовании

auto lam = [](int x) { return x * 2; };         // лямбда (C++11)
auto ref = [&sum](int x) { sum += x; };         // захват по ссылке
auto cpy = [=](int x) { return x + base; };     // захват по значению
auto mut = [base]() mutable { return ++base; }; // mutable: копию можно менять
auto gen = [](auto a, auto b) { return a + b; }; // generic-лямбда (C++14)
constexpr auto cl = [](int x) { return x * x; };// constexpr-лямбда (C++17)

std::function<int(int)> stored = [](int x) { return x; }; // стирание типа (дороже вызова)

void varargs(int n, ...);                       // C-стиль, небезопасно, избегайте
auto trailing(int x) -> decltype(x);            // trailing return type

Классы, ООП, специальные функции

class Point {
public:
    Point(int x, int y) : x_(x), y_(y) {}   // конструктор со списком инициализации
    Point() = default;                       // дефолтный конструктор (C++11)
    int x() const { return x_; }             // const-метод: не меняет объект
    void setX(int x) { x_ = x; }
    static int count;                        // общее для всех объектов поле
private:
    int x_{0}, y_{0};                        // инициализаторы полей по умолчанию
};
int Point::count = 0;                        // определение статического поля вне класса

class Shape {                                // абстрактный класс
public:
    virtual double area() const = 0;         // чисто виртуальная функция
    virtual ~Shape() = default;              // виртуальный деструктор — обязателен!
};
class Circle : public Shape {
public:
    double area() const override { return 3.14 * r_ * r_; }  // override ловит опечатки
    void draw() final;                       // final: запрет переопределения потомкам
private:
    double r_{1.0};
};

struct Vec {                                 // operator overloading
    double x, y;
    Vec operator+(const Vec& o) const { return {x + o.x, y + o.y}; }
    bool operator==(const Vec& o) const = default;    // C++20
    auto operator<=>(const Vec& o) const = default;   // «космический» оператор (C++20)
};
std::ostream& operator<<(std::ostream& os, const Vec& v) {
    return os << v.x << ',' << v.y;          // печать через std::cout
}

class Buffer {                               // «правило пяти»
public:
    Buffer(const Buffer&) = default;             // copy-конструктор
    Buffer(Buffer&&) noexcept = default;         // move-конструктор (C++11)
    Buffer& operator=(const Buffer&) = default;  // copy-присваивание
    Buffer& operator=(Buffer&&) noexcept = default; // move-присваивание
    ~Buffer() = default;                         // деструктор
};
// Правило 0: ничего не объявляем — компилятор сгенерирует корректно.
// Правило 3: объявили деструктор/copy — объявите все три.
// Правило 5: объявили один из пяти — объявите все пять.

Доступ: public — всем, protected — классу и наследникам, private — только классу. У class по умолчанию private, у struct — public.

Шаблоны и концепты

template<class T> T maxOf(T a, T b) { return a > b ? a : b; }  // T выводится
template<class T, int N> struct Array { T data[N]; };          // non-type параметр
template<typename T> using Vec2 = std::vector<T>;              // псевдоним шаблона

template<> struct is_int<int> { static constexpr bool value = true; }; // полная специализация
template<class T> struct is_int { static constexpr bool value = false; };
template<class T> struct is_ptr<T*> { /* ... */ };             // частичная специализация

template<class... Ts> struct Tuple {};                         // вариативный шаблон (C++11)
template<class... Ts> void print(Ts&&... vs) {                 // perfect forwarding
    (std::cout << ... << vs);                                  // свёртка (fold, C++17)
}

// Концепты (C++20): ограничения на типы с понятными ошибками
template<class T>
concept Addable = requires(T a, T b) { a + b; };
template<Addable T> T sum(T a, T b) { return a + b; }

if constexpr (std::is_integral_v<T>) { /* ветка только для целых */ } // C++17

Пространства имён

namespace net { int port = 80; }
namespace net { void connect(); }        // можно открыть повторно

net::port;                               // полное имя
using net::port;                         // ввести одно имя в текущую область
namespace nn = net;                      // псевдоним пространства имён
using namespace net;                     // ввести все имена (избегайте в заголовках)

inline namespace v1 { int api(); }       // версионирование API (C++11)

Управление памятью и умные указатели

int* raw = new int(5);          // сырое выделение в куче
delete raw;                     // освобождение одиночного объекта
int* arr = new int[10];
delete[] arr;                   // для массива — именно delete[]!

// Современный C++: владение выражается типом (C++11)
auto up = std::make_unique<int>(5);                 // unique_ptr: единоличный владелец
auto upArr = std::make_unique<int[]>(10);           // массив (C++14)
std::shared_ptr<int> sp = std::make_shared<int>(5); // shared_ptr: разделяемое владение
std::weak_ptr<int> wp = sp;                          // weak_ptr: наблюдает, не владеет
sp.use_count();                                      // число владельцев
up.get(); up.release(); up.reset();                  // доступ / отдать / сбросить

// unique_ptr нельзя копировать, только перемещать:
auto up2 = std::move(up);

Модель памяти: стек — быстрый, автоматический (локальные переменные); куча — new/delete или умные указатели; статическая память — глобальные и static. Строка/вектор хранят данные в куче, но сами управляют освобождением через RAII.

STL: контейнеры

std::vector<int> v = {1, 2, 3};       // динамический массив
v.push_back(4); v.emplace_back(5);    // добавить в конец (emplace строит на месте)
v.pop_back(); v.size(); v.empty(); v[0]; v.at(0); // доступ (at — с проверкой границ)
v.front(); v.back(); v.clear(); v.reserve(100);

std::array<int, 3> a = {1, 2, 3};     // фиксированный размер, на стеке
std::deque<int> dq; dq.push_front(1); // двухсторонняя очередь
std::list<int> lst; lst.push_front(1); // двусвязный список
std::forward_list<int> fl;             // односвязный список

std::map<std::string, int> m; m["key"] = 1;  // упорядоченное дерево, O(log n)
std::unordered_map<std::string, int> um;     // хеш-таблица, среднее O(1)
std::set<int> s = {3, 1, 2};                 // уникальные, отсортированы
std::unordered_set<int> us;                  // хеш-множество
std::multimap<int,int> mm; std::multiset<int> ms; // допускают дубликаты ключей

std::stack<int> st; st.push(1); st.top(); st.pop();     // LIFO-адаптер
std::queue<int> q; q.push(1); q.front(); q.pop();       // FIFO-адаптер
std::priority_queue<int> pq; pq.push(3); pq.top();      // куча (максимум сверху)

std::pair<int, int> p{1, 2}; p.first; p.second;         // пара
std::tuple<int, double, char> t{1, 2.0, 'a'};           // кортеж
std::get<0>(t);                                          // доступ по индексу

std::optional<int> opt;               // значение или «ничего» (C++17)
opt.has_value(); opt.value_or(0);     // проверка / значение по умолчанию
std::variant<int, std::string> var;   // типобезопасный union (C++17)
std::any anything = 42;               // значение любого типа (C++17)
std::span<int> view(v);               // невладеющий вид на диапазон (C++20)

Сложность операций

Контейнер Доступ Поиск Вставка/удаление Упорядочен
vector O(1) O(n) O(1) в конец (аморт.), O(n) в середину нет
deque O(1) O(n) O(1) с обоих концов нет
list O(n) O(n) O(1) при известном итераторе нет
map / set — O(log n) O(log n) да
unordered_map / unordered_set — O(1) среднее O(1) среднее нет
priority_queue O(1) top — O(log n) push/pop да (куча)

STL: алгоритмы и ranges

#include <algorithm>
#include <numeric>

std::sort(v.begin(), v.end());                    // сортировка (introsort)
std::sort(v.begin(), v.end(), std::greater<>{});  // по убыванию
std::reverse(v.begin(), v.end());
std::find(v.begin(), v.end(), 3);                 // вернуть итератор
std::count(v.begin(), v.end(), 3);
std::count_if(v.begin(), v.end(), [](int x){ return x > 0; });
std::any_of(v.begin(), v.end(), pred);
std::all_of(v.begin(), v.end(), pred);
std::transform(v.begin(), v.end(), out.begin(), [](int x){ return x * 2; });
std::copy(v.begin(), v.end(), out.begin());
std::fill(v.begin(), v.end(), 0);
std::remove_if(v.begin(), v.end(), pred);         // + erase = «erase-remove idiom»
std::unique(v.begin(), v.end());                  // убрать соседние дубликаты (после sort)
std::lower_bound(v.begin(), v.end(), 42);         // на отсортированном: первый >= 42
std::upper_bound(v.begin(), v.end(), 42);         // первый > 42
std::accumulate(v.begin(), v.end(), 0);           // сумма <numeric>
std::min_element(v.begin(), v.end());
std::max_element(v.begin(), v.end());

// Ranges (C++20): короче и без пар итераторов
std::ranges::sort(v);
auto evens = v | std::views::filter([](int x){ return x % 2 == 0; })
               | std::views::transform([](int x){ return x * x; });
for (int x : evens) std::cout << x << ' ';       // ленивые вычисления

Строки, ввод-вывод и файлы

#include <string>
#include <iostream>
#include <fstream>

std::string s = "hello";
s += " world"; s.size(); s.empty(); s.clear();
s.substr(0, 5);                  // подстрока
s.find("lo");                    // позиция или std::string::npos
s.replace(0, 5, "hi");
s.starts_with("he");             // C++20
int num = std::stoi("42");       // строка → число
std::string t = std::to_string(42);  // число → строка

std::cout << "x = " << x << '\n';    // вывод
std::cin >> x;                        // ввод до пробела
std::getline(std::cin, s);            // ввод строки целиком
std::cerr << "error\n";               // поток ошибок (небуферизованный)

std::ifstream in("in.txt");           // чтение из файла
if (in) { std::string line; while (std::getline(in, line)) { } }
std::ofstream out("out.txt");         // запись в файл
out << "data\n";
in.close(); out.close();              // закрываются и в деструкторе (RAII)

std::format("{} + {} = {}", a, b, a + b);  // форматирование (C++20, <format>)
std::print("{}", x);                        // вывод с форматированием (C++23, <print>)

Исключения

#include <stdexcept>

try {
    if (bad) throw std::runtime_error("something failed");
}
catch (const std::out_of_range& e) { std::cerr << e.what(); }  // конкретный тип
catch (const std::exception& e)    { std::cerr << e.what(); }  // базовый тип
catch (...)                        { /* поймать любое исключение */ }

// Иерархия std::exception: logic_error, runtime_error,
// out_of_range, invalid_argument, length_error, bad_alloc, ...
void f() noexcept;      // обещание не бросать (нарушение → std::terminate)

Правило: бросайте по значению, ловите по const&. Для владения ресурсами — RAII, а не try/finally.

Многопоточность

#include <thread>
#include <mutex>
#include <atomic>

std::thread t([]{ /* работа в отдельном потоке */ });
t.join();                 // дождаться (или t.detach() — отпустить)

std::mutex mtx;
{
    std::lock_guard<std::mutex> lk(mtx);   // захват/освобождение по scope (C++11)
    // критическая секция
}
std::unique_lock<std::mutex> ul(mtx);      // можно unlock()/lock() и передавать
std::scoped_lock sl(m1, m2);               // несколько мьютексов без deadlock (C++17)

std::atomic<int> counter{0};
counter.fetch_add(1);                      // атомарно, без мьютекса
counter++;                                 // тоже атомарно

std::jthread j([]{ /* ... */ });           // сам делает join (C++20)
j.request_stop();                          // кооперативная отмена
std::stop_token st = j.get_stop_token();

// std::condition_variable — ожидание события
// std::async / std::future / std::promise — результат из другого потока
auto fut = std::async(std::launch::async, []{ return 42; });
int result = fut.get();

Современный C++: быстрый обзор C++11–23

auto x = 42;                              // C++11: вывод типа
auto [a, b] = std::pair{1, 2};            // C++17: structured bindings
if (auto it = m.find(k); it != m.end()) { }  // C++17: if с инициализацией
switch (auto r = f(); r) { }              // C++17: switch с инициализацией
if constexpr (std::is_integral_v<T>) { }  // C++17: ветвление на этапе компиляции
static_assert(sizeof(int) == 4);          // C++11/17: проверка при компиляции
for (auto&& x : v) { }                    // forwarding-ссылка в range-for
constexpr auto sq = [](int x){ return x*x; }; // C++17: constexpr-лямбда
using namespace std::literals;            // UDL-литералы
auto d = 1s + 500ms;                      // chrono (C++14)
std::byte b8{0xFF};                       // C++17: байт
co_await; co_yield; co_return;            // C++20: корутины
requires Addable<T>;                      // C++20: requires-выражения
concept C = std::integral<T>;             // C++20: концепты
import std;                               // C++23: модули
std::expected<int, std::error_code> e;    // C++23: значение или ошибка
std::flat_map<int,int> fm;                // C++23: контейнерные адаптеры
std::mdspan m(ptr, 2, 3);                 // C++23: многомерный вид

Быстрая шпаргалка по подводным камням

  1. Правило 0/3/5. Не объявляйте специальные функции без нужды; объявили одну — объявите все.
  2. Виртуальный деструктор. Если удаляете наследника через указатель на базу — деструктор базы должен быть virtual.
  3. new → delete, new[] → delete[]. Перепутали — UB. В современном коде вообще не используйте сырые new/delete.
  4. std::move ничего не перемещает — лишь разрешает выбрать move-операцию. После перемещения не читайте источник.
  5. std::vector<bool> — особый случай: operator[] возвращает прокси, а не bool&.
  6. Инвалидация итераторов: push_back в vector может перевыделить буфер и убить все итераторы/ссылки.
  7. unsigned-арифметика уходит в огромные числа при вычитании (a - b < 0 не сработает).
  8. using namespace std; в заголовках — источник конфликтов имён. Держите его только в .cpp.
  9. -Wall -Wextra и санитайзеры — включайте с первого дня: они ловят то, что глазами не видно.
  10. volatile ≠ потокобезопасность. Для общих данных между потоками — std::atomic или мьютекс.

Что дальше

Практический совет. Не пытайтесь выучить справочник целиком. Держите его открытым рядом с cppreference и компилятором: когда забыли синтаксис — найдите блок, скопируйте, проверьте на компиляторе. Знание закрепляется через применение, а не через чтение.


Связанные шпаргалки: C++, C++ для студентов, C++ Core Guidelines, Си, Алгоритмы