← Назад к курсу
Подробный обзор ключевых аспектов 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, современные фичи) и избегать типичных подводных камней (ручное управление памятью, неявные приведения). Приведённые практики и примеры служат основой для построения надёжных, читаемых и безопасных проектов.