- Заголовочные файлы
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 { }