Code review на C++: что проверять, чтобы код не «взорвался»

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: почему code review на C++ — это не для слабонервных

C++ — это язык, который даёт программисту невероятную мощь. Но, как говорил дядя Бен из «Человека-паука»: «С великой силой приходит великая ответственность». И если в Python вы можете случайно смешать int и str и получить лишь исключение, то в C++ та же оплошность может привести к неопределённому поведению (UB), которое проявится через месяц на сервере заказчика в 3 часа ночи.

Code review на C++ — это не галочка в Jira. Это ваш щит от segmentation fault, утечек памяти и медленного, как улитка, кода. Многие разработчики (особенно те, кто пришёл из Java или Python) думают, что код-ревью — это проверка стиля и названий переменных. В C++ всё глубже. Здесь нужно смотреть в самую душу программы: на управление памятью, на RAII, на const-correctness и на то, не выстрелит ли этот умный указатель вам в ногу.

В этой статье я собрал реальный чек-лист для проверки кода C++. Берите его на вооружение, когда будете делать очередной code review. Поехали!

1. Утечки памяти и управление ресурсами (RAII)

Это — святая святых C++. Если вы видите new без delete — это красный флаг. Но в современном C++ (C++11 и новее) мы вообще не должны писать new вручную. Используйте умные указатели: std::unique_ptr, std::shared_ptr. Если разработчик использует сырые указатели (int*) для владения ресурсом — это повод задать вопрос.

Пример плохого кода (красный флаг):

void process() {
    int* data = new int[1000];
    // ... какой-то код ...
    if (error) {
        return; // УТЕЧКА! delete не вызван
    }
    delete[] data;
}

Пример хорошего кода (RAII):

void process() {
    std::vector<int> data(1000);
    // ... код ...
    if (error) {
        return; // Всё ок, вектор сам очистит память
    }
}

Что проверять в code review на C++:

  • Есть ли new/delete в коде? Если да — почему? Почему не std::make_unique?
  • Используются ли std::unique_ptr для эксклюзивного владения?
  • Не передаётся ли std::shared_ptr по значению туда, где достаточно ссылки (это дорого из-за atomic counter)?
  • Есть ли утечки при исключениях (exception safety)?

2. Неопределённое поведение (Undefined Behavior)

В C++ компилятор доверяет программисту. Если вы написали чушь, компилятор не будет вас ругать — он просто сгенерирует код, который может делать что угодно. UB — это бич C++. Вот что нужно ловить на ревью:

  • Выход за границы массива: arr[10] для массива из 10 элементов.
  • Использование переменной после перемещения: std::move — это не магия, объект после перемещения находится в валидном, но неопределённом состоянии.
  • Signed integer overflow: INT_MAX + 1 — это UB! (В отличие от Java, где это просто обёртка).
  • Dangling reference: возврат ссылки на локальную переменную.

Пример UB (ищите это на code review):

int& getValue() {
    int x = 42;
    return x; // UB! x уничтожен после выхода из функции
}

void example() {
    int& ref = getValue();
    std::cout << ref; // Может вывести 42, а может — мусор или краш
}

Совет для ревьюера: если видите арифметику с int, особенно в циклах или при работе с размерами — проверьте, не будет ли переполнения. Лучше использовать size_t для индексов и std::ssize() для безопасного сравнения.

3. Const-correctness: делайте всё const по умолчанию

Одна из самых недооценённых практик. Const-correctness — это когда вы помечаете const всё, что не должно меняться. Это помогает компилятору оптимизировать код и, что важнее, предотвращает глупые ошибки.

Что проверять:

  • Методы, которые не изменяют объект, должны быть помечены const.
  • Параметры, которые не изменяются, должны быть const & (а не передаваться по значению без нужды).
  • Локальные переменные, которые не изменяются — const.

Пример:

// Плохо: не const, хотя ничего не меняет
std::string getName() { return name; }

// Хорошо: const и возвращает копию (или const&)
const std::string& getName() const { return name; }

На code review C++ это сразу бросается в глаза. Если видите метод, который не помечен const, но не меняет состояние — это повод написать комментарий.

4. Работа с исключениями и безопасность

C++ не прощает наплевательского отношения к исключениям. Если в коде есть throw, убедитесь, что все ресурсы будут корректно освобождены. RAII здесь — ваш главный друг.

Что проверять:

  • Нет ли new в конструкторе без try-catch (если вылетит исключение — утечка).
  • Используются ли std::vector вместо сырых массивов (вектор сам очистится при исключении).
  • Не написан ли деструктор, который кидает исключение (это почти всегда UB).

5. Производительность: копирование vs перемещение

Современный C++ — это про zero-cost abstractions, но только если вы ими правильно пользуетесь. На code review часто видны лишние копирования.

Примеры:

// Плохо: лишнее копирование
void setData(std::vector<int> vec) { data_ = vec; }

// Хорошо: принимаем по значению и перемещаем
void setData(std::vector<int> vec) { data_ = std::move(vec); }

// Ещё лучше: const &
void setData(const std::vector<int>& vec) { data_ = vec; }

Что проверять: передаются ли тяжёлые объекты (строки, векторы) по значению без причины? Используется ли <

Блоги

Рекомендаций по книгам