Практические занятия по курсу ОиМП/C++ check

Материал из Wiki - Факультет компьютерных наук
Перейти к: навигация, поиск

Оформление

  1. Отступ - 2 или 4 пробела (используйте одинаковую ширину отступов во всёй программе). Символы табуляции использовать для отступов запрещено.
  2. Настройка в Visual Studio 2019: Средства → Параметры → Текстовый редактор → С/C++ → Табуляция → Галочка возле "Вставлять пробелы"

  3. Отступами выделяются: тела функций, структур/классов, вложенных блоков.
  4. Бинарные операторы отбиваются пробелами с двух сторон, после унарных пробелы не ставятся: a += b + -c
  5. Пробелы после открывающей скобки и перед закрывающей скобкой не ставятся: f(1, (2 + 3)). Закрывающая скобка должна идти на той же строке, что и последнее выражение.
  6. Максимальная длина строки - 100 символов.
  7. Перед ; пробел не ставится. После ; в for ставится пробел.
  8. Пустые блоки записываются как {} (а не ;).
  9. Открывающая { пишется на той же строке, что и начало блока (if, while, for, объявление функции).
  10. else пишется на той же строке, что и закрывающая } от if: if (...) {...\n} else {
  11. Однострочные комментарии отделяются от кода двумя пробелами и начинаются с пробела.
  12. Пробелы в конце строки запрещены.
  13. Файл должен заканчиваться переводом строки.
  14. В range-base for двоеточие обрамляется пробелами.
  15. В начале/конце блока, после public/private/protected пустые строки не ставятся.
  16. Перед объявлением функции/структуры/класса - пустая строка обязательна.
  17. Секции include-ов и using-ов были упорядочены по алфавиту. Плохо:
    #include <vector>
    #include <iostream>

    Хорошо:

    #include <iostream>
    #include <vector>

    Плохо:

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

    Хорошо:

    using std::cin;
    using std::cout;
    using std::vector;
  18. Имя шаблона и параметр шаблона НЕ должны разделяться пробелом. Плохо:
    vector <int> v;

    Хорошо:

    vector<int> v

Именование

  1. Имена переменных должны начинаться со строчной буквы.
    плохо хорошо
    int Distance;
    int distance;
  2. Не используйте однобуквенных переменных (за исключением счётчиков циклов). Из названия переменной должно быть понятно что в ней хранится.
    плохо хорошо
    // количество строк и столбцов
    size_t a, b;
    //количество строк и столбцов
    size_t rowCount, columnCount;
  3. Не используйте транслит в именах.
    плохо хорошо
    size_t dlina_massiva;
    size_t length;
  4. Давайте переменным осмысленные имена. В частности, не используйте однобуквенных имён за исключением имён итераторов (i, j, k, ...), координат (x, y, z).
    плохо хорошо
    char alf(int j) { 
        ...
    }
    // или
    char alphabet(int j) { 
        ...
    }
    char get_char(int j) {
        ...
    }
    int ln;
    int length;
  5. Для переменных-счётчиков не следует использовать имена DocNum, NumDoc, DocsCount, DocsNum, CountDoc из-за неграмотности и неоднозначности. Для числа элементов (скажем документов) можно использовать NumDocs или DocCount. Для функции, долго и явно подсчитывающей это число, подойдет CountDocs().
  6. Функциям, которые возвращают bool, лучше давать имена, начинающиеся на is или has.
    // плохо
    bool graph_connected() { ... }
    // хорошо
    bool is_connected_graph() { ... }
    // плохо
    bool element(int n) { ... }
    // хорошо
    bool has_element(int n) { ... }

std::vector

  1. Если требуется создать вектор размера n, воспользуйтесь специальным конструктором.
    плохо хорошо
    vector<int> v;
    v.resize(n);
    vector<int> v(n);

Циклы

  1. Если требуется перебрать элементы коллекции, предпочитайте range-base for. Он лаконичней и легче читается.
    // плохо
    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 int& neighbor_vertex : neighbors_list[vertex]) {
        if (!(visited[neighbor_vertex])) {
            dfs(neighbor_vertex, visited);
        }
    }

Функции

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

  • для избежания дублирования кода;
  • для того, чтобы можно было их переиспользовать много раз в различных проектах.
  1. Порядок аргументов функции: сначала входные параметры (по значению либо константой ссылке), затем выходные (по указателю).
  2. Передавайте аргументы в функции по константной ссылке везде, где это уместно.
    плохо хорошо
    void print_vector(vector<int> v) {
        ...
    }
    void print_vector(const vector<int>& v) {
        ...
    }
  3. Не создавайте функции с избыточным числом аргументов. Например, в функцию, печатающую вектор, не нужно передавать размер вектора.
    плохо хорошо
    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] << " ";
        }
    }
  4. В проекте есть вектор, хранящий путь
    vector<int> way
    и требуется написать функцию, которая печатает этот путь.
    плохо хорошо
    void print_way(const vector<int>& way) {
        for (size_t i = 0; i < way.size(); ++i) {
            cout << way[i] << " ";
        }
    }
    void print_vector(const vector<int>& v) {
        for (size_t i = 0; i < v.size(); ++i) {
            cout << v[i] << " ";
        }
    }
     
    // или
     
    void print_vector(const vector<int>& v) {
        for (size_t i = 0; i < v.size(); ++i) {
            cout << v[i] << " ";
        }
    }
     
    void print_way(const vector<int>& way) {
        print_vector(way);
    }

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

Прочее

  1. Необходимо явно подключать заголовочные файлы, в которых объявляются используемые функции/классы/... Запрещено явно подключать один и тот же заголовочный файл дважды.
  2. В качестве логических операторов следует использовать &&, ||, ... Их аналоги and, or, ... запрещены.
  3. Запрещено использовать приведение типов в стиле C - следует использовать *_cast.
  4. Конструктор от одного аргумента должен быть объявлен explicit.
  5. При объявлении виртуальной функции следует использовать один и только один из спецификаторов virtual, final, override.
  6. При объявлении переменной спецификаторы static/extern/... идут перед именем типа.
  7. Везде, где это возможно, используйте префиксный инкремент и декремент. Плохо:
    i++;
    it--;

    Хорошо:

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

Примеры и проверка

как делать не надо

Флаги для cpplint: --filter=-,+build/include,-build/include_order,-build/include_what_you_use,+build/storage_class,+readability/alt_tokens,+readability/braces,+readability/casting,+readability/inheritance,+runtime/casting,+runtime/explicit,+whitespace/blank_line,+whitespace/braces,+whitespace/comma,+whitespace/comments,+whitespace/empty_conditional_body,+whitespace/empty_loop_body,+whitespace/end_of_line,+whitespace/ending_newline,+whitespace/forcolon,+whitespace/indent,+whitespace/line_length,+whitespace/newline,+whitespace/operators,+whitespace/parens,+whitespace/semicolon,+whitespace/tab --linelength=100