Чистый код на C++: как писать так, чтобы не стыдно было через год

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

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

Начать курс

Почему чистый код на 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++. Рассмотрим ключевые: