Почему чистый код на C++ — это не про педантизм, а про уважение
Если вы когда-нибудь открывали проект, написанный «на коленке» год назад, и не могли понять, что делает функция с именем doStuff() — вы знаете, о чём речь. Чистый код на C++ — это не просто эстетика. Это экономия времени, нервов и денег. C++ — язык мощный, но и коварный: память управляется вручную, шаблоны могут порождать монстров, а перегрузка операторов — настоящую магию. Без дисциплины код быстро превращается в «спагетти».
В этой статье мы разберём, как писать чистый код на C++ так, чтобы его было легко читать, поддерживать и расширять. Никакой воды — только практика, примеры и проверенные принципы.
Правило №1: Именование — это 50% успеха
Названия переменных, функций и классов должны отвечать на вопрос «что это?» или «что делает?». Забудьте про int x и void f(). Используйте осмысленные имена.
Плохой пример
int d; // что такое d? дистанция? дата? дурак?
void p() { // печатает? парсит? плачет?
// ...
}
Хороший пример
int distanceInMeters;
void printUserProfile() {
// ...
}
Совет: Придерживайтесь одного стиля. Для C++ стандарт — camelCase для переменных и функций, PascalCase для классов. Не мешайте snake_case и camelCase в одном проекте — это снижает читаемость.
Правило №2: Функции должны быть маленькими и делать одно дело
Принцип единственной ответственности (Single Responsibility) работает не только для классов, но и для функций. Идеальная функция — 5–20 строк. Если функция делает больше одного действия — разбейте её.
Плохо
void processData(vector<int>& data) {
// сортировка
sort(data.begin(), data.end());
// фильтрация
vector<int> filtered;
for (int x : data) if (x > 0) filtered.push_back(x);
// вывод
for (int x : filtered) cout << x << " ";
}
Хорошо
vector<int> filterPositive(const vector<int>& data) {
vector<int> result;
for (int x : data) if (x > 0) result.push_back(x);
return result;
}
void printData(const vector<int>& data) {
for (int x : data) cout << x << " ";
}
void processData(vector<int>& data) {
sort(data.begin(), data.end());
auto filtered = filterPositive(data);
printData(filtered);
}
Теперь каждую функцию можно переиспользовать и тестировать отдельно. Чистый код на C++ — это модульность.
Правило №3: Избегайте магических чисел и строк
Числа и строки, значение которых непонятно из контекста, — зло. Выносите их в константы.
Плохо
if (temperature > 100) {
cout << "Опасно!";
}
Хорошо
constexpr int MAX_SAFE_TEMPERATURE = 100;
if (temperature > MAX_SAFE_TEMPERATURE) {
cout << "Опасно!";
}
Теперь, если порог изменится, вы поменяете его в одном месте. А не будете искать все 100 по коду.
Правило №4: Комментарии — не оправдание плохому коду
Лучший комментарий — тот, который не нужен, потому что код говорит сам за себя. Не пишите «что делает код» — это видно. Пишите «почему он это делает».
Плохой комментарий
// увеличиваем i на 1
i++;
Хороший комментарий
// Используем преинкремент для POD-типов — быстрее
++i;
Используйте // TODO: для незавершённых участков, но не злоупотребляйте. Идеальный код не требует пояснений.
Правило №5: Используйте RAII и умные указатели
C++ славится ручным управлением памятью, но в современном C++ (C++11 и новее) есть мощные инструменты, чтобы избежать утечек. RAII (Resource Acquisition Is Initialization) — ваш лучший друг.
Плохо
void oldWay() {
int* arr = new int[100];
// ... работаем
delete[] arr; // а если исключение? утечка!
}
Хорошо
void safeWay() {
std::unique_ptr<int[]> arr = std::make_unique<int[]>(100);
// ... работаем
// память освободится автоматически при выходе из функции
}
Используйте std::unique_ptr для единоличного владения, std::shared_ptr — для разделяемого. Забудьте про new и delete в явном виде. Это делает чистый код на C++ безопасным.
Правило №6: const — это не для слабаков
Помечайте как const всё, что не должно меняться: переменные, параметры, методы. Это защищает от случайных изменений и упрощает чтение.
class Rectangle {
public:
Rectangle(double w, double h) : width_(w), height_(h) {}
double area() const { // метод не меняет объект
return width_ * height_;
}
private:
double width_;
double height_;
};
Кроме того, const-корректность помогает компилятору оптимизировать код.
Правило №7: Избегайте глобальных переменных и side effects
Глобальные переменные делают код непредсказуемым. Функция, которая зависит от глобального состояния, — бомба замедленного действия. Передавайте всё явно через параметры.
Плохо
int globalCounter = 0;
void increment() {
globalCounter++;
}
Хорошо
class Counter {
public:
void increment() { count_++; }
int get() const { return count_; }
private:
int count_ = 0;
};
Правило №8: Принципы SOLID для C++
Принципы SOLID применимы и к C++. Рассмотрим ключевые:
- S — Single Responsibility: класс отвечает за одну задачу.
- O — Open/Closed: открыт для расширения, закрыт для изменения.
- L — Liskov Substitution: наследники не должны ломать логику.