Skip to content

Latest commit

 

History

History
465 lines (393 loc) · 16.2 KB

File metadata and controls

465 lines (393 loc) · 16.2 KB

Оформление

  • Между операторами и операндами ставятся пробелы
int a = (b + c) / d * e + foo();
  • Пробелы после открывающей скобки и перед закрывающей скобкой не ставятся. Закрывающая скобка должна идти на той же строке, что и последнее выражение.
int a = f(1, (2 + 3));
  • Максимальная длина строки - 80 символов.
  • Перед ; пробел не ставится. После ; в for ставится пробел.
  • Тела условий и циклов выделяются {} даже если тело блока состоит лишь из одной строки. Пустые блоки записываются как {} (а не ;).
for (size_t i = 0; i < n; ++i) {
  sum += i;
}
  • Однострочные комментарии отделяются от кода двумя пробелами и начинаются с пробела.
  • Пробелы в конце строки запрещены.
  • Файл должен заканчиваться переводом строки.
  • В range-based for двоеточие обрамляется пробелами.
  • В начале/конце блока, после public/private/protected пустые строки не ставятся.
  • Перед объявлением функции/структуры/класса — пустая строка обязательна.
  • Секции include-ов и using-ов должны быть упорядочены по алфавиту.

Плохо:

using std::vector;
using std::cin;
using std::cout;

Хорошо:

using std::cin;
using std::cout;
using std::vector;
  • Не надо вставлять заголовочные файлы там, где они не требуются. Следует выносить их в cpp файлы, в которых они требуются.

Пример:

// my_lib.hpp

#include <list>
#include <vector> // если вырезать эту строчку из заголовочного файла, он не сломается -> следует её вынести в my_lib.cpp

std::list my_function(int a, int b);
  • Заголовочные файлы вставляются в следующем порядке:

    1. Парный файл (lib.h для lib.cpp, например) — в ""
    2. Пустая строка
    3. Системные header файлы C с расширением .h (если такие вдруг требуются) — в <>
    4. Пустая строка
    5. Системные header файлы стандартной библиотеки C++ — в <>
    6. Пустая строка
    7. header файлы .h других библиотек — в ""
    8. header файлы вашего проекта — в ""
  • Одинаковые по значимости заголовочные файлы должны распологаться в алфавитном порядке.

Плохо:

#include "stdlib.h"
#include <lib.h>
#include <cstring>
#include <vector>
#include "user_lib_2.h"
#include <iostream>
#include "parser.h"

Хорошо:

#include "lib.h"

#include <stdlib.h>

#include <cstring>
#include <iostream>
#include <vector>

#include "parser.h"
#include "user_lib_2.h"
  • Имя шаблона и параметр шаблона НЕ должны разделяться пробелом.

Плохо:

vector <int> v;

Хорошо:

vector<int> v;

Именование

  • Имена переменных во всём проекте должны соответствовать одному стилю (snake_case или camelCase, например), для классов можно использовать PascalCase.
  • Имена переменных должны начинаться со строчной буквы.

Плохо

int Distance;

Хорошо

int distance;
  • Объявляйте каждую переменную на отдельной строке. Переменные встроенных типов данных необходимо сразу инициализировать.
  • Не используйте однобуквенных переменных (за исключением счётчиков циклов, итераторов (i, j, k, ...), координат (x, y, z)).

Плохо

// количество строк и столбцов
size_t a, b;

Хорошо

// количество строк и столбцов
size_t rowCount = 0;
size_t columnCount = 0;
  • Давайте переменным осмысленные имена. Из названия переменной должно быть понятно, что в ней хранится. Из названия функции должно быть понятно, что она делает.

Плохо

char alf(int j) {
    ...
}
// или
char alphabet(int j) {
    ...
}
int ln = n; // длина

Хорошо

char get_char(int j) {
    ...
}
int length = n; // длина
  • Не используйте транслит в именах.

Плохо

size_t dlina_massiva = 42;

Хорошо

size_t array_len = 42;
  • Для переменных-счётчиков не следует использовать имена docNum, numDoc, docsCount, docsNum, countDoc из-за неграмотности и неоднозначности. Для числа элементов (скажем документов) можно использовать numDocs или docCount. Для функции, долго и явно подсчитывающей это число, подойдет countDocs().

  • Функциям, которые возвращают bool, лучше давать имена, начинающиеся на is или has.

Плохо

bool graph_connected() { ... }
bool element(int n) { ... }

Хорошо

bool is_connected_graph() { ... }
bool has_element(int n) { ... }

Циклы

  • Если требуется перебрать элементы коллекции, предпочитайте range-based for. Он лаконичней и легче читается.
  • Если элементы коллекции должны измениться, можно использовать тип auto&, если элементы не должны меняться — const auto&

Плохо

for (size_t i = 0; i < neighbors_list[vertex].size(); ++i) {
    if (!(visited[neighbors_list[vertex][i]])) {
        dfs(neighbors_list[vertex][i], visited);
    }
}

Хорошо

for (const auto& neighbor_vertex : neighbors_list[vertex]) {
    if (!(visited[neighbor_vertex])) {
        dfs(neighbor_vertex, visited);
    }
}

Функции

Функции нужны:

  1. для избежания дублирования кода;
  2. для того, чтобы можно было их использовать множество раз в различных проектах.
  • Порядок аргументов функции: сначала входные параметры (по значению, ссылке либо константой ссылке), затем выходные.

  • Отправляя объект в функцию как изменяемый параметр, вы должны передавать его по ссылке.

  • Если вы передаете объект в функцию и код не изменит вида объекта — передайте его по значению, если это встроенный тип данных, иначе — как const-ссылку.

Плохо

void print_vector(vector<int> v) {
    ...
}

Хорошо

void print_vector(const vector<int>& v) {
    ...
}

Плохо

void print_first_n_elem(const size_t& n, vector<int>& v) {
    ...
}

Хорошо

void print_first_n_elem(const vector<int>& v, size_t n) {
    ...
}

Code Style Formatter

В CLion его можно настроить следующим образом:

File -> Settings (либо CTRL+ALT+S) -> Editor -> Code Style -> C/C++ (выбрать, раскрыв панель Code Style слева) -> Set from -> Google -> OK

По умолчанию работает сочетание клавиш CTRL+ALT+L, настроить его при желании можно:

File -> Settings (либо CTRL+ALT+S) -> Keymap -> Main Menu -> Code -> Reformat Code


Подсказки

  • Если требуется создать std::vector размера n, воспользуйтесь специальным конструктором.

Плохо

vector<int> v;
v.resize(n);

Хорошо

vector<int> v(n);
  • Простые функции без циклов длиной менее 10 строк лучше объявлять inline, это может повысить производительность кода:
inline int sqr(int input) {
  return input * input;
}
  • Не создавайте функции с избыточным числом аргументов. Например, в функцию, печатающую вектор, не нужно передавать размер вектора.

Плохо

void print_vector(const vector<int>& v, int n) {
    for (size_t i = 0; i < n; ++i) {
        cout << v[i] << " ";
    }
}

Хорошо

void print_vector(const vector<int>& v) {
    // Используйте v.size(), чтобы получить длину вектора
    for (size_t i = 0; i < v.size(); ++i) {
        cout << v[i] << " ";
    }
}
  • В проекте есть вектор, хранящий путь
vector<int> way

и требуется написать функцию, которая печатает этот путь.

Плохо

void print_way(const vector<int>& way) {
    for (int i : way) {
        cout << i << " ";
    }
}

Хорошо

void print_vector(const vector<int>& v) {
    for (int i : v) {
        cout << i << " ";
    }
}

Тоже хорошо

void print_vector(const vector<int>& v) {
    for (int i : v) {
        cout << i << " ";
    }
}

void print_way(const vector<int>& way) {
    print_vector(way);
}

Плохой вариант плох тем, что теряется свойство переиспользования у функции: вы не сможете использовать её в другом проекте, где, скажем, нужно будет распечатать вектор оценок.

  • Другой пример — найти самое длинное и самое короткое из трех имен

Плохо

  ...
  if (name_one.length() > name_two.length() &&
      name_one.length() > name_three.length()) {
    the_longest = name_one;
  } else if (name_two.length() > name_one.length() &&
             name_two.length() > name_three.length()) {
    the_longest = name_two;
  } else if (name_three.length() > name_one.length() &&
             name_three.length() > name_two.length()) {
    the_longest = name_three;
  }

  if (name_one.length() < name_two.length() &&
      name_one.length() < name_three.length()) {
    the_shortest = name_one;
  } else if (name_two.length() < name_one.length() &&
           name_two.length() < name_three.length()) {
    the_shortest = name_two;
  } else if (name_three.length() < name_one.length() &&
           name_three.length() < name_two.length()) {
    the_shortest = name_three;
  }
  ...

Хорошо

  ...
  std::vector<std::string> names({name_one, name_two, name_three});
  sort(names);
  the_shortest = names[0];
  the_longest = names[names.size() - 1]

В отличие от примера с ветвлением функцию с массивом будет легче перестроить под другие исходные данные.

Прочее

  • Старайтесь не использовать глобальные переменные.
  • Необходимо явно подключать заголовочные файлы, в которых объявляются используемые функции/классы/... Запрещено явно подключать один и тот же заголовочный файл дважды.
  • Если реализация фукнции приведена в source.cpp файле, а объявлена функция в header.h, те библиотеки, без которых сам по себе header может обойтись, лучше подключать сразу в source.cpp
  • В качестве логических операторов следует использовать &&, ||, ... Их аналоги and, or, ... запрещены.
  • Запрещено использовать приведение типов в стиле СИ — следует использовать *_cast.
  • Конструктор от одного аргумента должен быть объявлен explicit.
  • При объявлении виртуальной функции следует использовать один и только один из спецификаторов virtual, final, override.
  • При объявлении переменной спецификаторы static/extern/... идут перед именем типа.
  • Везде, где это возможно, используйте префиксный инкремент и декремент.

Плохо

i++;
it--;

Хорошо

++i;
--it;
  • Пишите код так, чтобы не было предупреждений (warnings) компилятора. Это нужно по двум причинам:
    1. Обычно компилятор выдаёт предупреждения по делу, на те места, где скрыта потенциальная ошибка;
    2. Если игнорировать "неважные" предупреждения, они могут накопиться и вы не заметите действительно важных.
  • В частности, избегайте сравнений знаковых (int) и беззнаковых (size_t) переменных.

Плохо

for (int i = 0; i < v.size(); ++i) {
    ...
}

Хорошо

for (size_t i = 0; i < v.size(); ++i) {
    ...
}
  • Если у вас есть выражение if / else, которое возвращает логическое значение, возвращайте результаты теста напрямую:

Плохо

if (list.Head == nullptr) {
    return true;
} else {
    return false;
}

Хорошо

return list.Head == nullptr;
  • Не проверяйте bool значения, используя == или != в связке с true или false:

Плохо

if (x == true) {
    ...
} else if (x != true) {
    ...
}

Хорошо

if (x) {
    ...
} else {
    ...
}
  • Не нужно создавать новую переменную для возврата результата из фукнции:

Плохо

  ...
  float res = x + y / z;
  return res;
}

Хорошо

  ...
  return x + y / z;
}

Источники

  • Для составления инструкции были использованы материалы по ссылке
  • Если хочется немного ГОСТа, можно глянуть Google C++ Style Guide