- Заголовочные файлы
1.1. Include guards
1.2. Подключайте то, что используете
1.3. Порядок подключения - Области видимости
2.1. Пространства имён
2.2. Локальные переменные - Классы
- Функции
- Наименование и синтаксис
5.1. Файлы
5.2. Классы и структуры
5.3. Функции и методы
5.4. Перечисления
5.5. Пространства имён
В общем случае каждому файлу исходного кода (.cpp
) должен соответствовать заголовочный файл (.h
). Бывают исключения - например, файл main.cpp
, который содержит точку входа - функцию main
Каждый заголовочный файл должен содержать include guard - конструкцию, которая не позволяет подключать файл более одного раза
Если для реализации include guard используются директивы #ifndef
и #define
, то название символьной константы должно составляться следующим образом: <PROJECT>_<PATH>_<FILE>_H_
Например, для файла foo\src\bar\baz.h
в проекте foo
:
Хорошо
#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_
...
#endif // FOO_BAR_BAZ_H_
Плохо
#ifndef BAZ
#define BAZ
...
#endif // BAZ
Если вы пользуетесь какой-то сущностью, объявленной извне - подключайте тот заголовочный файл, в котором эта сущность объявлена. Больше никаких причин для подключения заголовочного файла не существует
Не надейтесь на транзитивные подключения (когда заголовочный файл, который содержит нужную вам сущность, уже подключён в другом заголовочном файле, который вы уже подключили)
// bar.h
# pragma once
class Bar { }
// foo.h
# pragma once
#include "bar.h"
class Foo {
public:
Foo(Bar bar);
}
Хорошо
// main.cpp
#include "foo.h"
#include "bar.h"
int main(int argc, char** argv) {
Bar bar;
Foo foo(bar);
}
Плохо
// main.cpp
#include "foo.h"
int main(int argc, char** argv) {
Bar bar;
Foo foo(bar);
}
Подключайте заголовочные файлы в следующем порядке:
- ассоциированный с текущим файлом исходного кода заголовочный файл
- системные заголовочные файлы C
- заголовочные файлы стандартной библиотеки C++
- заголовочные файлы остальных библиотек
- заголовочные файлы вашего проекта
Все вышеперечисленные группы разделяются пустой строкой (в случае, если они не пустые)
Избегайте использования относительных директорий (.
и ..
) для определения пути к заголовочному файлу вашего проекта - указывайте путь, начиная с корня проекта
Например, блок подключения заголовочных файолов в файле foo-project/src/foo/internal/fooserver.cpp
Хорошо
#include "foo/server/fooserver.h"
#include <sys/types.h>
#include <unistd.h>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "foo/server/bar.h"
#include "third_party/absl/flags/flag.h"
Плохо
#include <sys/types.h>
#include "foo/server/bar.h"
#include "third_party/absl/flags/flag.h"
#include <unistd.h>
#include "base/basictypes.h"
#include <string>
#include "foo/server/fooserver.h"
#include <vector>
Правило, для которого есть редкие исключения, гласит - всегда пишите код внутри пространства имён
Пространства имён должны иметь уникальное название, основанное на названии проекта и, по возможности, пути внутри проекта
Не используйте директивы using
(например, using namespace foo
)
Не используйте inline
пространства имён
Инициализируйте локальные переменные при объявлении Хорошо
int j = g();
Плохо
int i;
i = f();
Используйте списки инициализации Хорошо
std::vector<int> v = {1, 2};
Плохо
std::vector<int> v;
v.push_back(1);
v.push_back(2);
Объявляйте переменные там, где они нужны - в самой узкой области видимости, которая возможна Хорошо
while (const char* p = strchr(str, '/')) str = p + 1;
Плохо
const char* p = nullptr;
while (p = strchr(str, '/')) str = p + 1;
Есть исключение из этого правила - если переменная является экземпляром класса, и вы объявляете (и создаёте) её в цикле, то конструктор и деструктор этого класса будет вызываться при каждой итерации цикла, что негативно скажется на производительности
Хорошо
Foo f;
for (int i = 0; i < 1000000; ++i) {
f.DoSomething(i);
}
Плохо
for (int i = 0; i < 1000000; ++i) {
Foo f;
f.DoSomething(i);
}
Избегайте вызовов виртуальных методов внутри конструктора - во время работы конструктора объект ещё не создан полностью
Избегайте неявных преобразований. Используйте ключевое слово explicit
для операторов преобразования и конструкторов с одним аргументом
Используйте структуры (struct
) для классов без поведения, которые нужны только для хранения данных. В остальных случаях используйте классы (class
)
Композиция (использование экземпляра одного класса внутри другого) часто является более подходящей, нежели наследование. При использовании наследования - делайте его публичным (public
)
Определение класса обычно начинается с секции public
, затем идёт protected
и потом private
Внутри каждой секции группируйте члены класса одного рода, придерживаясь следующего порядка:
- Типы и псевдонимы типов (
typedef
,using
,enum
, внутренние структуры и классы, дружественные (friend
) типы - Статические константы
- Фабричные методы
- Конструкторы и операторы присвоения
- Деструктор
- Остальные методы (статические, нестатические, дружественные)
- Поля (статические и нестатические)
Предпочительнее возвращать результат работы функции с помощью возвращаемого значения, чем с помощью выходного параметра
Хорошо
int sum(int a, int b);
Плохо
void sum(int a, int b, int& sum);
Лучше возвращать результат по значению. Если это невозможно, то лучше по ссылке, чем с помощью указателя. Возвращать значение с помощью указателя можно в случае, если оно может быть равно nullptr
Предпочтительнее использовать маленькие однозадачные функции. Если функция получается длинной, подумайте о том, можно ли её разбить на несколько
Не стоит использовать аргументы по умолчанию в виртуальных методах, так как наследники могут переопределить значение по умолчанию, что в итоге может запутать программиста
Все наименования могут содержать в себе буквы латинского алфавита, цифры и символ _
. При этом никакое название не может начинаться с цифры
Открывающие фигурные скобки следует располагать на той же самой строке, а не на следующей
Внутри блока, заключённого в фигурных скобках, нужно делать отступ - 4 пробела от предыдущего уровня
Хорошо
void myFunction1() {
int myVar2 = 5;
}
Плохо
void 1myFunction()
{
int 2myVar = 5;
}
Название файла должно начинаться с маленькой буквы, отдельные слова разделяются нижним подчёркиванием Хорошо
main.cpp
my_header.h
very_complex_name.cpp
Плохо
Main.cpp
my-header.h
veryComplexName.cpp
Название класса или структуры должно начинаться с большой буквы, отдельные слова начинаются с большой буквы и не отделяются символами Хорошо
class MyAwesomeClass {};
Плохо
class my_awesome_class {};
class myAwesomeClass {};
class My_Awesome_Class {};
Название полей в классе или структуре должно начинаться с маленькой буквы, отдельные слова начинаются с большой буквы и не отделяются символами
Хорошо
class MyClass {
int myField;
};
Плохо
class MyClass {
int MyField;
int my_field;
int My_Field;
};
Название константных полей в классе или структуре нужно писать только большими буквами, разделяя отдельные слова нижним подчёркиванием
Хорошо
class MyClass {
const int MY_CONST_FIELD;
};
Плохо
class MyClass {
const int myConstField;
const int MyConstField;
const int my_const_field;
const int My_Const_Field;
};
Название свободной функции или метода (функции - члена класса) должно начинаться с маленькой буквы, отдельные слова начинаются с большой буквы и не отделяются символами Хорошо
void myFreeFunction();
class MyAwesomeClass {
void myMethod();
};
Плохо
void MyFreeFunction();
void my_free_function();
void My_Free_Function();
Название аргументов функций и методов должно начинаться с маленькой буквы, отдельные слова начинаются с большой буквы и не отделяются символами
Хорошо
void myFreeFunction(int firstArg, float secondArg);
Плохо
void myFreeFunction(int first_arg, float second_arg);
void myFreeFunction(int FirstArg, float SecondArg);
void myFreeFunction(int First_Arg, float Second_Arg);
Название локальных переменных функций и методов должно начинаться с маленькой буквы, отдельные слова начинаются с большой буквы и не отделяются символами
Хорошо
void myFreeFunction(int firstArg, float secondArg) {
int myVar;
}
Плохо
void myFreeFunction(int firstArg, float secondArg) {
int my_var;
int MyVar;
int My_Var;
}
Название перечисления должно начинаться с большой буквы, отдельные слова начинаются с большой буквы и не отделяются символами Названия констант внутри перечисления нужно писать только большими буквами, разделяя отдельные слова нижним подчёркиванием
Хорошо
enum class MyEnum {
MY_CONST_1,
MY_CONST_2,
MY_CONST_3,
};
Плохо
enum class MY_ENUM {
my_const_1,
MyConst2,
My_Const_3,
};
Название пространства имён должно начинаться с маленькой буквы, отдельные слова начинаются с маленькой буквы и отделяются символом _
Хорошо
namespace my_namespace { }
Плохо
namespace My_Namespace { }
namespace MyNamespace { }
namespace MY_NAMESPACE { }