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: многомерный вид
Быстрая шпаргалка по подводным камням
- Правило 0/3/5. Не объявляйте специальные функции без нужды; объявили одну — объявите все.
- Виртуальный деструктор. Если удаляете наследника через указатель на базу — деструктор базы должен быть
virtual. new→delete,new[]→delete[]. Перепутали — UB. В современном коде вообще не используйте сырыеnew/delete.std::moveничего не перемещает — лишь разрешает выбрать move-операцию. После перемещения не читайте источник.std::vector<bool>— особый случай:operator[]возвращает прокси, а неbool&.- Инвалидация итераторов:
push_backвvectorможет перевыделить буфер и убить все итераторы/ссылки. unsigned-арифметика уходит в огромные числа при вычитании (a - b < 0не сработает).using namespace std;в заголовках — источник конфликтов имён. Держите его только в.cpp.-Wall -Wextraи санитайзеры — включайте с первого дня: они ловят то, что глазами не видно.volatile≠ потокобезопасность. Для общих данных между потоками —std::atomicили мьютекс.
Что дальше
- C++ с нуля: большая шпаргалка по синтаксису, ООП, STL и современным стандартам — глубокий разбор с пояснениями и ловушками.
- C++ для студентов: шпаргалка к экзамену — что спрашивают на коллоквиуме и контрольных (МИФИ, МГУ, МФТИ).
- C++ Core Guidelines: перевод ключевых правил — как писать современный и безопасный C++.
- Си (C): большая шпаргалка по синтаксису, указателям и памяти — фундамент, из которого вырос C++.
- Алгоритмы на C/C++: шпаргалка — структуры данных и типовые задачи.
Практический совет. Не пытайтесь выучить справочник целиком. Держите его открытым рядом с cppreference и компилятором: когда забыли синтаксис — найдите блок, скопируйте, проверьте на компиляторе. Знание закрепляется через применение, а не через чтение.
Связанные шпаргалки: C++, C++ для студентов, C++ Core Guidelines, Си, Алгоритмы