Руководство и документация по языку программирования C++
Фундаментальный разбор языка C++ и стандарта ISO: трансляция кода, системное управление памятью, RAII, шаблоны (Templates), Move-семантика, STL и стандарты C++17/C++20/C++23.
-Wall -Wextra -Werror или /W4) и ориентируйтесь на современный C++ (Modern C++), избегая сырых указателей и опасных си-стилей.
- 1. Экосистема C++, препроцессинг и линковка (Toolchain)
- 2. Управление памятью: Стек, Куча, RAII и Умные указатели
- 3. ООП, Правило пяти и Семантика перемещения (Move Semantics)
- 4. Шаблонное программирование (Templates) и Концепты (Concepts)
- 5. Стандартная библиотека шаблонов (STL) и алгоритмы
- 6. Многопоточность, асинхронность и атомарные операции
1. Экосистема C++, препроцессинг и линковка (Toolchain)
Язык программирования C++ — это высокопроизводительный компилируемый язык общего назначения с прямой поддержкой низкоуровневых операций над памятью и процессором. Процесс превращения исходного текста программ (файлы .cpp и .hpp) в исполняемый бинарный файл включает три ключевых этапа:
- Препроцессинг (Preprocessing): Препроцессор обрабатывает директивы, начинающиеся с символа
#(например,#include,#define,#ifdef). Он подставляет содержимое заголовочных файлов и разворачивает макросы. - Компиляция (Compilation): Компилятор анализирует единицу трансляции, строит абстрактное синтаксическое дерево (AST), оптимизирует код и генерирует объектные файлы (файлы
.oили.obj), содержащие машинный код. - Компоновка / Линковка (Linking): Линковщик объединяет все объектные файлы и внешние статические (
.a/.lib) или динамические (.so/.dll) библиотеки в единый исполняемый модуль.
2. Управление памятью: Стек, Куча, RAII и Умные указатели
В C++ отсутствует автоматический сборщик мусора (Garbage Collector). Разработчик напрямую контролирует жизненный цикл каждого байта памяти. Память программы логически разделена на стек (Stack) и кучу (Heap / Free Store):
- Стек (Stack): Высокоскоростная память для локальных переменных. Память выделяется и освобождается автоматически при выходе из области видимости (Scope).
- Куча (Heap): Динамическая память. Выделяется вручную с помощью оператора
newи должна быть обязательно освобождена операторомdelete(во избежание утечек памяти / Memory Leaks).
Для гарантированного предотвращения утечек ресурсов в современном C++ используется парадигма RAII (Resource Acquisition Is Initialization) и умные указатели (Smart Pointers) из заголовка <memory>:
#include <iostream>
#include <memory>
#include <string>
class UserProfile {
private:
int id;
std::string username;
public:
UserProfile(int id, std::string name)
: id(id), username(std::move(name)) {}
void displayInfo() const {
std::cout << "ID: " << id << " | User: " << username << "\n";
}
};
void executeUserSession() {
// RAII: Уникальное владение объектом в куче. Автоматически удалится при выходе из функции
auto user = std::make_unique<UserProfile>(101, "Alex");
user->displayInfo();
}
std::unique_ptr реализует монопольное владение ресурсом (его нельзя скопировать, только переместить). std::shared_ptr использует счетчик ссылок для разделяемого владения, а std::weak_ptr предотвращает циклические ссылки.
3. ООП, Правило пяти и Семантика перемещения (Move Semantics)
ООП в C++ поддерживает множественное наследование, виртуальные функции и полиморфизм через таблицы виртуальных методов (vtable). Начиная со стандарта C++11, фундаментальным улучшением стала семантика перемещения (Move Semantics), избавляющая от дорогостоящего глубокого копирования данных.
Если класс вручную управляет ресурсами (указателями, файловыми дескрипторами), он должен соблюдать Правило пяти (Rule of Five) — явно определить или запретить 5 специальных функций-членов:
#include <utility>
class BufferManager {
private:
size_t size;
int* data;
public:
// 1. Конструктор
explicit BufferManager(size_t s) : size(s), data(new int[s]{}) {}
// 2. Деструктор
~BufferManager() { delete[] data; }
// 3. Конструктор копирования
BufferManager(const BufferManager& other) : size(other.size), data(new int[other.size]) {
for (size_t i = 0; i < size; ++i) data[i] = other.data[i];
}
// 4. Перемещающий конструктор (Rvalue reference &&)
BufferManager(BufferManager&& other) noexcept
: size(other.size), data(other.data) {
other.size = 0;
other.data = nullptr;
}
// 5. Операторы присваивания (копирующий и перемещающий)
BufferManager& operator=(BufferManager&& other) noexcept {
if (this != &other) {
delete[] data;
size = other.size;
data = other.data;
other.size = 0;
other.data = nullptr;
}
return *this;
}
};
4. Шаблонное программирование (Templates) и Концепты (Concepts)
Шаблоны (Templates) обеспечивают метапрограммирование на этапе компиляции. В отличие от дженериков в Java/C#, шаблоны C++ генерируют отдельный оптимизированный код под каждый тип аргумента (Monomorphization), что исключает накладные расходы во время выполнения (Zero-cost Abstractions).
Начиная с C++20, ограничения шаблонов проверяются с помощью Концептов (Concepts), что кардинально улучшает понятность ошибок компилятора:
#include <concepts>
#include <iostream>
// Определение концепта: тип должен поддерживать сложение и сравнение
template <typename T>
concept Numeric = std::is_arithmetic_v<T>;
// Определение концепта: тип должен поддерживать сложение и сравнение
template <typename T>
concept Numeric = std::is_arithmetic_v<T>;
// Функция, ограниченная концептом Numeric
template <Numeric T>
T calculateSum(T a, T b) {
return a + b;
}
int main() {
std::cout << calculateSum(10, 20) << "\n"; // Успешно: int
// calculateSum("A", "B"); // Ошибка компиляции: string не удовлетворяет Numeric
}
5. Стандартная библиотека шаблонов (STL) и алгоритмы
STL (Standard Template Library) предоставляет мощный набор готовых контейнеров, итераторов и алгоритмов. Выбор верного контейнера определяет временную сложность операций (Big O):
| Контейнер STL | Структура данных | Временная сложность (Доступ / Вставка) |
|---|---|---|
std::vector<T> |
Динамический непрерывный массив | Доступ: O(1) | Вставка в конец: O(1) amortized |
std::unordered_map<K, V> |
Хеш-таблица (Hash Table) | Поиск/Вставка: O(1) average, O(N) worst case |
std::map<K, V> |
Красно-черное дерево (Red-Black Tree) | Поиск/Вставка: O(log N) — всегда отсортирован |
std::span<T> (C++20) |
Невладеющее представление массива | Zero-allocation обертка над непрерывной памятью |
Использование диапазонных алгоритмов (Ranges library из C++20):
#include <vector>
#include <algorithm>
#include <ranges>
#include <iostream>
void processScores() {
std::vector<int> scores = {15, 42, 8, 23, 90, 4};
// Декларативная фильтрация и трансформация с помощью C++20 Ranges
auto results = scores
| std::views::filter([](int n) { return n >= 10; })
| std::views::transform([](int n) { return n * 2; });
for (int val : results) {
std::cout << val << " "; // Выведет: 30 84 46 180
}
}
6. Многопоточность, асинхронность и атомарные операции
Язык C++ поддерживает нативную модель многопоточности начиная со стандарта C++11 через библиотеку <thread>. Для синхронизации потоков и защиты критических секций от состояния гонки (Data Race) используются мьютексы (std::mutex, std::scoped_lock) и атомарные типы (std::atomic<T>).
#include <thread>
#include <mutex>
#include <future>
#include <iostream>
std::mutex g_num_mutex;
int g_counter = 0;
void safeIncrement() {
// RAII-защита мьютекса: захватывает при создании, освобождает при выходе из скоупа
std::lock_guard<std::mutex> lock(g_num_mutex);
++g_counter;
}
int asyncCompute(int input) {
return input * 42;
}
void runConcurrencyExample() {
// Запуск асинхронной задачи с получением std::future
std::future<int> futureResult = std::async(std::launch::async, asyncCompute, 10);
// Выполнение работы в главном потоке...
int res = futureResult.get(); // Ожидание и получение результата (420)
std::cout << "Асинхронный результат: " << res << "\n";
}