- Между операторами и операндами ставятся пробелы
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);-
Заголовочные файлы вставляются в следующем порядке:
- Парный файл (
lib.hдляlib.cpp, например) — в"" - Пустая строка
- Системные header файлы
Cс расширением .h (если такие вдруг требуются) — в<> - Пустая строка
- Системные header файлы стандартной библиотеки
C++— в<> - Пустая строка
- header файлы .h других библиотек — в
"" - 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);
}
}Функции нужны:
- для избежания дублирования кода;
- для того, чтобы можно было их использовать множество раз в различных проектах.
-
Порядок аргументов функции: сначала входные параметры (по значению, ссылке либо константой ссылке), затем выходные.
-
Отправляя объект в функцию как изменяемый параметр, вы должны передавать его по ссылке.
-
Если вы передаете объект в функцию и код не изменит вида объекта — передайте его по значению, если это встроенный тип данных, иначе — как 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) {
...
}В 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) компилятора. Это нужно по двум причинам:
- Обычно компилятор выдаёт предупреждения по делу, на те места, где скрыта потенциальная ошибка;
- Если игнорировать "неважные" предупреждения, они могут накопиться и вы не заметите действительно важных.
- В частности, избегайте сравнений знаковых (
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