Введение: почему 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; }
Что проверять: передаются ли тяжёлые объекты (строки, векторы) по значению без причины? Используется ли <