Практические занятия по курсу ОиМП/C++ check — различия между версиями
(→Оформление) |
|||
(не показаны 23 промежуточные версии 3 участников) | |||
Строка 1: | Строка 1: | ||
== Оформление == | == Оформление == | ||
<ol> | <ol> | ||
− | <li>Отступ - 4 пробела. Символы табуляции использовать для отступов запрещено.</li> | + | <li>Отступ - 2 или 4 пробела (используйте одинаковую ширину отступов во всёй программе). Символы табуляции использовать для отступов запрещено.</li> |
+ | Настройка в Visual Studio 2019: | ||
+ | Средства → Параметры → Текстовый редактор → С/C++ → Табуляция → Галочка возле "Вставлять пробелы" | ||
<li>Отступами выделяются: тела функций, структур/классов, вложенных блоков.</li> | <li>Отступами выделяются: тела функций, структур/классов, вложенных блоков.</li> | ||
<li>Бинарные операторы отбиваются пробелами с двух сторон, после унарных пробелы не ставятся: a += b + -c</li> | <li>Бинарные операторы отбиваются пробелами с двух сторон, после унарных пробелы не ставятся: a += b + -c</li> | ||
Строка 53: | Строка 55: | ||
vector<int> v | vector<int> v | ||
</source></li> | </source></li> | ||
+ | </ol> | ||
== Именование == | == Именование == | ||
+ | <ol> | ||
+ | <li>Имена переменных должны начинаться со строчной буквы. | ||
+ | {| | ||
+ | |- | ||
+ | ! !! плохо !! хорошо | ||
+ | |- | ||
+ | ! | ||
+ | |<source lang="cpp">int Distance;</source> | ||
+ | |<source lang="cpp">int distance;</source> | ||
+ | |} | ||
+ | </li></li> | ||
+ | <li>Не используйте однобуквенных переменных (за исключением счётчиков циклов). Из названия переменной должно быть понятно что в ней хранится. | ||
+ | {| | ||
+ | |- | ||
+ | ! !! плохо !! хорошо | ||
+ | |- | ||
+ | ! | ||
+ | |<source lang="cpp">// количество строк и столбцов | ||
+ | size_t a, b;</source> | ||
+ | |<source lang="cpp">//количество строк и столбцов | ||
+ | size_t rowCount, columnCount;</source> | ||
+ | |} | ||
+ | </li> | ||
<li>Не используйте транслит в именах. | <li>Не используйте транслит в именах. | ||
− | + | {| | |
− | <source lang="cpp"> | + | |- |
− | size_t dlina_massiva; | + | ! !! плохо !! хорошо |
− | </source> | + | |- |
− | + | ! | |
− | + | |<source lang="cpp">size_t dlina_massiva;</source> | |
− | <source lang="cpp"> | + | |<source lang="cpp">size_t length;</source> |
− | size_t length; | + | |} |
− | </source></li> | + | </li> |
− | + | ||
<li>Давайте переменным осмысленные имена. В частности, не используйте однобуквенных имён за исключением имён итераторов (i, j, k, ...), координат (x, y, z). | <li>Давайте переменным осмысленные имена. В частности, не используйте однобуквенных имён за исключением имён итераторов (i, j, k, ...), координат (x, y, z). | ||
− | + | {| | |
− | + | |- | |
− | <source lang="cpp"> | + | ! !! плохо !! хорошо |
+ | |- | ||
+ | ! | ||
+ | |<source lang="cpp"> | ||
char alf(int j) { | char alf(int j) { | ||
... | ... | ||
Строка 79: | Строка 107: | ||
} | } | ||
</source> | </source> | ||
− | + | |<source lang="cpp"> | |
− | + | ||
− | <source lang="cpp"> | + | |
char get_char(int j) { | char get_char(int j) { | ||
... | ... | ||
} | } | ||
</source> | </source> | ||
− | + | |- | |
− | + | ! | |
− | <source lang="cpp"> | + | |<source lang="cpp"> |
int ln; | int ln; | ||
</source> | </source> | ||
− | + | |<source lang="cpp"> | |
− | + | ||
− | <source lang="cpp"> | + | |
int length; | int length; | ||
− | </source></li> | + | </source> |
+ | |}</li> | ||
− | + | <li>Для переменных-счётчиков не следует использовать имена DocNum, NumDoc, DocsCount, DocsNum, CountDoc из-за неграмотности и неоднозначности. Для числа элементов (скажем документов) можно использовать NumDocs или DocCount. Для функции, долго и явно подсчитывающей это число, подойдет CountDocs().</li> | |
− | + | ||
− | + | <li>Функциям, которые возвращают bool, лучше давать имена, начинающиеся на is или has. | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
<source lang="cpp"> | <source lang="cpp"> | ||
− | + | // плохо | |
− | + | bool graph_connected() { ... } | |
− | } | + | |
</source> | </source> | ||
− | |||
− | |||
<source lang="cpp"> | <source lang="cpp"> | ||
− | + | // хорошо | |
− | + | bool is_connected_graph() { ... } | |
− | } | + | |
</source> | </source> | ||
− | |||
− | |||
− | |||
<source lang="cpp"> | <source lang="cpp"> | ||
− | + | // плохо | |
− | + | bool element(int n) { ... } | |
</source> | </source> | ||
+ | <source lang="cpp"> | ||
+ | // хорошо | ||
+ | bool has_element(int n) { ... } | ||
+ | </source></li> | ||
+ | </ol> | ||
− | + | == std::vector == | |
+ | <ol> | ||
+ | <li>Если требуется создать вектор размера n, воспользуйтесь специальным конструктором. | ||
+ | {| | ||
+ | |- | ||
+ | ! !! плохо !! хорошо | ||
+ | |- | ||
+ | ! | ||
+ | |<source lang="cpp">vector<int> v; | ||
+ | v.resize(n);</source> | ||
+ | |<source lang="cpp">vector<int> v(n);</source> | ||
+ | |} | ||
+ | </li> | ||
+ | </ol> | ||
+ | |||
+ | == Циклы == | ||
+ | <ol> | ||
+ | <li>Если требуется перебрать элементы коллекции, предпочитайте [http://ru.cppreference.com/w/cpp/language/range-for range-base for]. Он лаконичней и легче читается. | ||
<source lang="cpp"> | <source lang="cpp"> | ||
− | ++i | + | // плохо |
− | + | for (size_t i = 0; i < neighbors_list[vertex].size(); ++i) { | |
+ | if (!(visited[neighbors_list[vertex][i]])) { | ||
+ | dfs(neighbors_list[vertex][i], visited); | ||
+ | } | ||
+ | } | ||
</source> | </source> | ||
− | |||
− | |||
<source lang="cpp"> | <source lang="cpp"> | ||
− | void print_vector(const vector<int>& v, int n) { | + | // хорошо |
+ | for (const int& neighbor_vertex : neighbors_list[vertex]) { | ||
+ | if (!(visited[neighbor_vertex])) { | ||
+ | dfs(neighbor_vertex, visited); | ||
+ | } | ||
+ | } | ||
+ | </source></li> | ||
+ | </ol> | ||
+ | |||
+ | == Функции == | ||
+ | Функции нужны | ||
+ | * для избежания дублирования кода; | ||
+ | * для того, чтобы можно было их переиспользовать много раз в различных проектах. | ||
+ | |||
+ | <ol> | ||
+ | <li>Порядок аргументов функции: сначала входные параметры (по значению либо константой ссылке), затем выходные (по указателю).</li> | ||
+ | <li>Передавайте аргументы в функции по константной ссылке везде, где это уместно. | ||
+ | {| | ||
+ | |- | ||
+ | ! !! плохо !! хорошо | ||
+ | |- | ||
+ | ! | ||
+ | |<source lang="cpp">void print_vector(vector<int> v) { | ||
+ | ... | ||
+ | }</source> | ||
+ | |<source lang="cpp">void print_vector(const vector<int>& v) { | ||
+ | ... | ||
+ | }</source> | ||
+ | |} | ||
+ | </li> | ||
+ | <li>Не создавайте функции с избыточным числом аргументов. Например, в функцию, печатающую вектор, не нужно передавать размер вектора. | ||
+ | {| | ||
+ | |- | ||
+ | ! !! плохо !! хорошо | ||
+ | |- | ||
+ | ! | ||
+ | |<source lang="cpp">void print_vector(const vector<int>& v, int n) { | ||
for (size_t i = 0; i < n; ++i) { | for (size_t i = 0; i < n; ++i) { | ||
+ | cout << v[i] << " "; | ||
+ | } | ||
+ | }</source> | ||
+ | |<source lang="cpp">void print_vector(const vector<int>& v) { | ||
+ | // Используйте v.size(), чтобы получить длину вектора | ||
+ | for (size_t i = 0; i < v.size(); ++i) { | ||
+ | cout << v[i] << " "; | ||
+ | } | ||
+ | }</source> | ||
+ | |} | ||
+ | </li> | ||
+ | <li>В проекте есть вектор, хранящий путь <source lang="cpp">vector<int> way</source> и требуется написать функцию, которая печатает этот путь. | ||
+ | {| | ||
+ | |- | ||
+ | ! !! плохо !! хорошо | ||
+ | |- | ||
+ | ! | ||
+ | |<source lang="cpp">void print_way(const vector<int>& way) { | ||
+ | for (size_t i = 0; i < way.size(); ++i) { | ||
+ | cout << way[i] << " "; | ||
+ | } | ||
+ | }</source> | ||
+ | |<source lang="cpp">void print_vector(const vector<int>& v) { | ||
+ | for (size_t i = 0; i < v.size(); ++i) { | ||
cout << v[i] << " "; | cout << v[i] << " "; | ||
} | } | ||
} | } | ||
− | |||
− | + | // или | |
− | + | ||
void print_vector(const vector<int>& v) { | void print_vector(const vector<int>& v) { | ||
− | |||
for (size_t i = 0; i < v.size(); ++i) { | for (size_t i = 0; i < v.size(); ++i) { | ||
cout << v[i] << " "; | cout << v[i] << " "; | ||
} | } | ||
+ | } | ||
+ | |||
+ | void print_way(const vector<int>& way) { | ||
+ | print_vector(way); | ||
} | } | ||
</source> | </source> | ||
− | + | |} | |
+ | Плохой вариант плох тем, что теряется свойство реюзабельности у функции: вы не сможете использовать её в другом проекте, где, скажем, нужно будет распечатать вектор оценок. | ||
+ | </li> | ||
+ | </ol> | ||
+ | |||
+ | == Прочее == | ||
+ | <ol> | ||
+ | <li>Необходимо явно подключать заголовочные файлы, в которых объявляются используемые функции/классы/... Запрещено явно подключать один и тот же заголовочный файл дважды.</li> | ||
+ | <li>В качестве логических операторов следует использовать &&, ||, ... Их аналоги and, or, ... запрещены.</li> | ||
+ | <li>Запрещено использовать приведение типов в стиле C - следует использовать *_cast.</li> | ||
+ | <li>Конструктор от одного аргумента должен быть объявлен explicit.</li> | ||
+ | <li>При объявлении виртуальной функции следует использовать один и только один из спецификаторов virtual, final, override.</li> | ||
+ | <li>При объявлении переменной спецификаторы static/extern/... идут перед именем типа.</li> | ||
+ | <li>Везде, где это возможно, используйте префиксный инкремент и декремент. | ||
Плохо: | Плохо: | ||
<source lang="cpp"> | <source lang="cpp"> | ||
− | + | i++; | |
− | + | it--; | |
− | + | ||
</source> | </source> | ||
Хорошо: | Хорошо: | ||
<source lang="cpp"> | <source lang="cpp"> | ||
− | + | ++i; | |
+ | --it; | ||
+ | </source></li> | ||
+ | <li>Пишите код так, чтобы не было предупреждений (warnings) компилятора. Это нужно по двум причинам: | ||
+ | # Обычно компилятор выдаёт предупреждения по делу, на те места, где скрыта потенциальная ошибка. | ||
+ | # Если игнорировать "неважные" предупреждения, их может много накопиться и вы не заметите действительно важных. | ||
+ | В частности, избегайте сравнений знаковых (int) и беззнаковых (size_t) переменных. | ||
+ | {| | ||
+ | |- | ||
+ | ! !! плохо !! хорошо | ||
+ | |- | ||
+ | ! | ||
+ | |<source lang="cpp">for (int i = 0; i < v.size(); ++i) { | ||
... | ... | ||
− | } | + | }</source> |
− | </source> | + | |<source lang="cpp">for (size_t i = 0; i < v.size(); ++i) { |
− | + | ... | |
+ | }</source> | ||
+ | |} | ||
+ | </li> | ||
+ | </ol> | ||
== Примеры и проверка == | == Примеры и проверка == | ||
[https://yadi.sk/d/0ZeUy2xKbyeuw как делать не надо] | [https://yadi.sk/d/0ZeUy2xKbyeuw как делать не надо] | ||
− | Флаги для [ | + | Флаги для [https://github.com/google/styleguide/blob/gh-pages/cpplint/cpplint.py 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 |
Текущая версия на 14:14, 14 января 2020
Оформление
- Отступ - 2 или 4 пробела (используйте одинаковую ширину отступов во всёй программе). Символы табуляции использовать для отступов запрещено.
- Отступами выделяются: тела функций, структур/классов, вложенных блоков.
- Бинарные операторы отбиваются пробелами с двух сторон, после унарных пробелы не ставятся: a += b + -c
- Пробелы после открывающей скобки и перед закрывающей скобкой не ставятся: f(1, (2 + 3)). Закрывающая скобка должна идти на той же строке, что и последнее выражение.
- Максимальная длина строки - 100 символов.
- Перед ; пробел не ставится. После ; в for ставится пробел.
- Пустые блоки записываются как {} (а не ;).
- Открывающая { пишется на той же строке, что и начало блока (if, while, for, объявление функции).
- else пишется на той же строке, что и закрывающая } от if: if (...) {...\n} else {
- Однострочные комментарии отделяются от кода двумя пробелами и начинаются с пробела.
- Пробелы в конце строки запрещены.
- Файл должен заканчиваться переводом строки.
- В range-base for двоеточие обрамляется пробелами.
- В начале/конце блока, после public/private/protected пустые строки не ставятся.
- Перед объявлением функции/структуры/класса - пустая строка обязательна.
- Секции 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;
- Имя шаблона и параметр шаблона НЕ должны разделяться пробелом.
Плохо:
vector <int> v;
Хорошо:
vector<int> v
Настройка в Visual Studio 2019: Средства → Параметры → Текстовый редактор → С/C++ → Табуляция → Галочка возле "Вставлять пробелы"
Именование
- Имена переменных должны начинаться со строчной буквы.
плохо хорошо int Distance;
int distance;
- Не используйте однобуквенных переменных (за исключением счётчиков циклов). Из названия переменной должно быть понятно что в ней хранится.
плохо хорошо // количество строк и столбцов size_t a, b;
//количество строк и столбцов size_t rowCount, columnCount;
- Не используйте транслит в именах.
плохо хорошо size_t dlina_massiva;
size_t length;
- Давайте переменным осмысленные имена. В частности, не используйте однобуквенных имён за исключением имён итераторов (i, j, k, ...), координат (x, y, z).
плохо хорошо char alf(int j) { ... } // или char alphabet(int j) { ... }
char get_char(int j) { ... }
int ln;
int length;
- Для переменных-счётчиков не следует использовать имена DocNum, NumDoc, DocsCount, DocsNum, CountDoc из-за неграмотности и неоднозначности. Для числа элементов (скажем документов) можно использовать NumDocs или DocCount. Для функции, долго и явно подсчитывающей это число, подойдет CountDocs().
- Функциям, которые возвращают bool, лучше давать имена, начинающиеся на is или has.
// плохо bool graph_connected() { ... }
// хорошо bool is_connected_graph() { ... }
// плохо bool element(int n) { ... }
// хорошо bool has_element(int n) { ... }
std::vector
- Если требуется создать вектор размера n, воспользуйтесь специальным конструктором.
плохо хорошо vector<int> v; v.resize(n);
vector<int> v(n);
Циклы
- Если требуется перебрать элементы коллекции, предпочитайте 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); } }
Функции
Функции нужны
- для избежания дублирования кода;
- для того, чтобы можно было их переиспользовать много раз в различных проектах.
- Порядок аргументов функции: сначала входные параметры (по значению либо константой ссылке), затем выходные (по указателю).
- Передавайте аргументы в функции по константной ссылке везде, где это уместно.
плохо хорошо void print_vector(vector<int> v) { ... }
void print_vector(const vector<int>& v) { ... }
- Не создавайте функции с избыточным числом аргументов. Например, в функцию, печатающую вектор, не нужно передавать размер вектора.
плохо хорошо 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 (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); }
Плохой вариант плох тем, что теряется свойство реюзабельности у функции: вы не сможете использовать её в другом проекте, где, скажем, нужно будет распечатать вектор оценок.
Прочее
- Необходимо явно подключать заголовочные файлы, в которых объявляются используемые функции/классы/... Запрещено явно подключать один и тот же заголовочный файл дважды.
- В качестве логических операторов следует использовать &&, ||, ... Их аналоги and, or, ... запрещены.
- Запрещено использовать приведение типов в стиле C - следует использовать *_cast.
- Конструктор от одного аргумента должен быть объявлен explicit.
- При объявлении виртуальной функции следует использовать один и только один из спецификаторов virtual, final, override.
- При объявлении переменной спецификаторы static/extern/... идут перед именем типа.
- Везде, где это возможно, используйте префиксный инкремент и декремент.
Плохо:
i++; it--;
Хорошо:
++i; --it;
- Пишите код так, чтобы не было предупреждений (warnings) компилятора. Это нужно по двум причинам:
- Обычно компилятор выдаёт предупреждения по делу, на те места, где скрыта потенциальная ошибка.
- Если игнорировать "неважные" предупреждения, их может много накопиться и вы не заметите действительно важных.
плохо хорошо 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