← Назад к курсу

Подробный обзор ключевых аспектов C++

1. История C++

Год Событие Что изменилось
1979‑1983 Создание C++ от Бьерна Страуструпа (AT&T) Добавлен объектно‑ориентированный синтаксис, указатели, функции, поддержка абстракции через классы.
1985 Cfront – первая реализация, переводящая C++ в C Позволил компиляцию на любом C‑компиляторе.
1990 C++98 (ISO/IEC 14882:1998) Появление стандарта, введения STL, templates, namespace.
2003 C++03 – поправки к C++98 Добавлены небольшие исправления, но основная модель осталась.
2011 C++11 (ISO/IEC 14882:2011) Усовершенствования: auto, nullptr, move semantics, smart pointers, lambda‑выражения, constexpr, enum class, range‑based for, std::thread.
2014 C++14 Упрощённые лямбды, улучшения constexpr, generic lambdas, std::make_unique.
2017 C++17 structured bindings, if constexpr, std::optional, std::variant, parallel STL, filesystem.
2020 C++20 Концепты, coroutines, ranges, modules, deducing this, spaceship operator (operator<=>).
2023‑2025 C++23 (частично) std::expected, std::generator, улучшения в std::format, std::syncstream.

Итог: Язык прошёл путь от небольшого набора расширений к современному, модульному и выразительному средству с мощными библиотечными и языковыми средствами.


2. Основы синтаксиса

Элемент Пример Комментарий
Один файл, один translation unit #include <iostream>
int main(){ std::cout << "Hello"; return 0; }
main – точка входа.
Комментарии // однострочный
/* многострочный */
Стандарт C++14 поддерживает // везде, /**/ – для блочных.
Переменные int a = 5;
double pi = 3.14;
Инициализация – обязательно для избежания неопределённого состояния.
Константы constexpr double c = 299792458.0; Вычисляются на этапе компиляции.
Операторы a = b + c;
if (a > 0) …
for (int i = 0; i < N; ++i)
Большинство – унаследованы от C, но есть новые (auto, nullptr).
Синтаксис функций int add(int a, int b){ return a + b; }
auto multiply = [](int a, int b){ return a * b; };
Лямбда‑выражения позволяют создавать анонимные функции в месте вызова.
Ключевое слово namespace namespace utils{ void foo(); } Предотвращает конфликты имён, может быть вложенным.
Управление памятью int* p = new int(42); delete p;
std::unique_ptr<int> up = std::make_unique<int>(42);
new/delete – ручное, smart pointers – автоматическое.

3. Типы данных

3.1 Встроенные типы

Категория Типы Пример
Целые bool, char, signed char, unsigned char, int8_t, uint8_t, short, int, long, long long bool flag = true;
Плавающие float, double, long double double radius = 2.5;
Буквенные char16_t, char32_t, wchar_t char32_t u = U'Ω';
Указатели/специальные nullptr_t, void*, std::size_t, std::ptrdiff_t std::size_t len = std::strlen(s);
Логический bool if (x) …

Правила выравнивания:

  • Размер типа зависит от платформы (обычно 1, 2, 4, 8 байт).
  • sizeof(T) – стандартный оператор, который возвращает размер типа в байтах.

3.2 Контейнерные типы из <cstdint>

Тип Биты Применение
int8_t 8 Кросс‑платформенные 8‑битные целые.
uint16_t 16 Беззнаковый, 2 байта.
int32_t 32 По умолчанию int в большинстве систем.
uint64_t 64 64‑битные беззнаковые.

4. Управление памятью

Техника Описание Пример
Stack (автоматическое) Переменные, размещаемые в стеке, arr[5]; Объём ограничен, быстрый доступ.
Heap (динамическое) new/delete int* p = new int(10); delete p;
Smart pointers std::unique_ptr, std::shared_ptr auto up = std::make_unique<MyClass>();
RAII Ресурсы (файлы, сетевые соединения) привязываются к объектам, которые автоматически освобождают их в деструкторе. std::ofstream f("log.txt"); – файл закрывается при выходе.
Placement new Размещение объекта в уже выделенном буфере. new (buffer) MyClass();

Рекомендация: почти всегда отдавать предпочтение smart pointers и RAII вместо ручного new/delete.


5. Классы и объектно‑ориентированное программирование (ООП)

5.1 Основные компоненты

  • Атрибуты (члены данных): int x; std::string name;
  • Методы (функции‑члены): void setX(int v){ x = v; }
  • Конструкторы: MyClass(int v): x(v){}
  • Деструктор: ~MyClass(){}
  • Наследование: class Derived : public Base { … }
  • Полиморфизм: виртуальные функции, абстрактные классы.
  • Инкапсуляция: private, protected, public.

5.2 Пример

class BankAccount {
public:
    BankAccount(int init = 0) : balance(init) {}
    void deposit(int amount) { balance += amount; }
    void withdraw(int amount) {
        if (amount > balance) throw std::runtime_error("Insufficient funds");
        balance -= amount;
    }
    int getBalance() const { return balance; }
private:
    int balance;
};

class SavingsAccount : public BankAccount {
public:
    using BankAccount::BankAccount; // наследуем конструктор
    void applyInterest(double rate) { balance *= (1 + rate); }
};

6. Шаблоны (templates)

  • Функциональные шаблоны: template<typename T> T add(T a, T b){ return a + b; }
  • Классовые шаблоны: template<class T, size_t N> class Buffer{ T data[N]; };
  • Шаблоны с несколькими параметрами: template<class T, class U, class V> struct Triple;
  • Концепты (C++20): ограничивают типы, требующие определенных свойств (std::integral, std::convertible_to).

Пример концепта:

template<typename T>
concept Addable = requires(T a, T b) {
    { a + b } -> std::convertible_to<T>;
};

template<Addable T>
T sum(T a, T b) { return a + b; }

7. Стандартная библиотека (STL)

Категория Пример Описание
Контейнеры std::vector<int>, std::array<double,3>, std::list<std::string> Динамические и статические контейнеры, ассоциативные std::map, std::unordered_map.
Итераторы auto it = std::find(v.begin(), v.end(), 42); Интерфейс доступа к элементам, работает единообразно с контейнерами.
Алгоритмы std::sort(v.begin(), v.end()); Операции над контейнерами, реализованы как шаблоны.
Функциональные объекты std::function<int(int)> f = [](int x){ return x*x; }; Универсальный тип для функций, лямбд, указателей.
Ввод‑вывод std::ifstream in("file.txt");
std::cout << "Hello" << std::endl;
Потоковый интерфейс, поддерживает широкий спектр типов.
Утилиты std::chrono, std::filesystem, std::optional, std::variant Расширенные возможности, появились в C++11/17/14/20.

8. Современные фичи C++

Фича Где появилась Краткое описание
auto C++11 Автоматическое вывод типа, упрощает объявления.
nullptr C++11 Нулевой указатель‑тип, предотвращает неявные преобразования к int.
constexpr C++11 Вычисления в compile‑time.
Lambda‑выражения C++11 Анонимные функции, часто используются в алгоритмах.
Range‑based for C++11 Упрощённый цикл по контейнерам.
std::unique_ptr / std::shared_ptr C++11 Управление памятью без утечек.
enum class C++11 Строгая типизация перечислений, отсутствие «int‑fallback».
std::make_shared / make_unique C++11 Безопасное создание объектов.
if constexpr C++17 Условная компиляция внутри шаблонов.
structured bindings C++17 Деструктуризация кортежей и структур.
std::optional C++17 Позволяет представлять значение «нет» без bool.
std::variant C++17 Полиморфный тип‑union, безопасный «any».
std::filesystem C++17 Работа с файловой системой.
concepts C++20 Ограничения на типы шаблонов, читаемый и проверяемый код.
coroutines C++20 Асинхронные функции, упрощают работу с асинхронными операциями.
ranges C++20 Библиотека <ranges> для удобного манипулирования диапазонами.
modules (предварительно) C++20 Переопределение заголовочных файлов, ускоряет компиляцию.
spaceship operator (operator<=>) C++20 Одно место для реализации всех сравнений (==, !=, <, <=, >, >=).
std::format (C++23) C++23 Текстовый форматирование без потоков.

9. Лучшие практики

Практика Обоснование Пример
RAII Гарантирует освобождение ресурсов, даже в случае исключений. std::ifstream file("data.txt");
Избегать ручного new/delete Снижает риск утечек, делает код чистым. std::vector<int> v;
Prefer const и constexpr Повышает читаемость и позволяет компилятору оптимизировать. const std::string name = "Alice";
Использовать enum class Предотвращает неявные преобразования и уменьшает путаницу. enum class LogLevel {Info, Warning, Error};
Соблюдать «Rule of 0/3/5» Если класс управляет ресурсом, определяйте деструктор, копирующий/перемещающий конструкторы и операторы присваивания. class Handle { … }
Писать модульные тесты Позволяет быстро находить регрессии. GoogleTest, Catch2.
Избегать C‑стили (глобальных функций без типов) Ошибки, связанные с неявными приведениями типов. int foo(int);
Статический анализ (clang‑tidy, cppcheck) Выявляет потенциальные баги и стилистические проблемы. clang-tidy source.cpp -- -std=c++20
Использовать nullptr вместо 0 Типизация и избежание ошибок. int* p = nullptr;
std::optional вместо указателей nullptr Явный тип «может быть пустым». std::optional<std::string> name;
std::variant вместо union Безопасный и типобезопасный. std::variant<int, double, std::string> val;
auto с осторожностью Не ухудшайте читаемость кода, где тип важен. auto sum = [&](){ … };
Не использовать using namespace std; в заголовочных файлах Предотвращает конфликты имён. using std::cout; только в реализации.
Документировать публичные интерфейсы Упрощает поддержку и обучение. Doxygen‑комментарии.

Заключение
C++ – мощный и гибкий язык, позволяющий писать системный, высокопроизводительный код. Понимание истории и эволюции языка помогает правильно выбирать подходы (RAII, современные фичи) и избегать типичных подводных камней (ручное управление памятью, неявные приведения). Приведённые практики и примеры служат основой для построения надёжных, читаемых и безопасных проектов.