Skip to content

Latest commit

 

History

History

cpp

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

Правила оформления C++ кода

  1. Заголовочные файлы
    1.1. Include guards
    1.2. Подключайте то, что используете
    1.3. Порядок подключения
  2. Области видимости
    2.1. Пространства имён
    2.2. Локальные переменные
  3. Классы
  4. Функции
  5. Наименование и синтаксис
    5.1. Файлы
    5.2. Классы и структуры
    5.3. Функции и методы
    5.4. Перечисления
    5.5. Пространства имён

1. Заголовочные файлы

В общем случае каждому файлу исходного кода (.cpp) должен соответствовать заголовочный файл (.h). Бывают исключения - например, файл main.cpp, который содержит точку входа - функцию main

1.1. Include guards

Каждый заголовочный файл должен содержать 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

1.2. Подключайте то, что используете

Если вы пользуетесь какой-то сущностью, объявленной извне - подключайте тот заголовочный файл, в котором эта сущность объявлена. Больше никаких причин для подключения заголовочного файла не существует

Не надейтесь на транзитивные подключения (когда заголовочный файл, который содержит нужную вам сущность, уже подключён в другом заголовочном файле, который вы уже подключили)

// 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);
}

1.3. Порядок подключения

Подключайте заголовочные файлы в следующем порядке:

  • ассоциированный с текущим файлом исходного кода заголовочный файл
  • системные заголовочные файлы 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>

2. Области видимости

2.1. Пространства имён

Правило, для которого есть редкие исключения, гласит - всегда пишите код внутри пространства имён

Пространства имён должны иметь уникальное название, основанное на названии проекта и, по возможности, пути внутри проекта

Не используйте директивы using (например, using namespace foo)

Не используйте inline пространства имён

2.2. Локальные переменные

Инициализируйте локальные переменные при объявлении Хорошо

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);
}

3. Классы

Избегайте вызовов виртуальных методов внутри конструктора - во время работы конструктора объект ещё не создан полностью

Избегайте неявных преобразований. Используйте ключевое слово explicit для операторов преобразования и конструкторов с одним аргументом

Используйте структуры (struct) для классов без поведения, которые нужны только для хранения данных. В остальных случаях используйте классы (class)

Композиция (использование экземпляра одного класса внутри другого) часто является более подходящей, нежели наследование. При использовании наследования - делайте его публичным (public)

Определение класса обычно начинается с секции public, затем идёт protected и потом private Внутри каждой секции группируйте члены класса одного рода, придерживаясь следующего порядка:

  1. Типы и псевдонимы типов (typedef, using, enum, внутренние структуры и классы, дружественные (friend) типы
  2. Статические константы
  3. Фабричные методы
  4. Конструкторы и операторы присвоения
  5. Деструктор
  6. Остальные методы (статические, нестатические, дружественные)
  7. Поля (статические и нестатические)

4. Функции

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

Хорошо

int sum(int a, int b);

Плохо

void sum(int a, int b, int& sum);

Лучше возвращать результат по значению. Если это невозможно, то лучше по ссылке, чем с помощью указателя. Возвращать значение с помощью указателя можно в случае, если оно может быть равно nullptr

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

Не стоит использовать аргументы по умолчанию в виртуальных методах, так как наследники могут переопределить значение по умолчанию, что в итоге может запутать программиста

5. Наименование и синтаксис

Все наименования могут содержать в себе буквы латинского алфавита, цифры и символ _. При этом никакое название не может начинаться с цифры

Открывающие фигурные скобки следует располагать на той же самой строке, а не на следующей

Внутри блока, заключённого в фигурных скобках, нужно делать отступ - 4 пробела от предыдущего уровня

Хорошо

void myFunction1() {
    int myVar2 = 5;
}

Плохо

void 1myFunction()
{
int 2myVar = 5;
}

5.1. Файлы

Название файла должно начинаться с маленькой буквы, отдельные слова разделяются нижним подчёркиванием Хорошо

main.cpp
my_header.h
very_complex_name.cpp

Плохо

Main.cpp
my-header.h
veryComplexName.cpp

5.2. Классы и структуры

Название класса или структуры должно начинаться с большой буквы, отдельные слова начинаются с большой буквы и не отделяются символами Хорошо

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;
};

5.3. Функции и методы

Название свободной функции или метода (функции - члена класса) должно начинаться с маленькой буквы, отдельные слова начинаются с большой буквы и не отделяются символами Хорошо

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;
}

5.4. Перечисления

Название перечисления должно начинаться с большой буквы, отдельные слова начинаются с большой буквы и не отделяются символами Названия констант внутри перечисления нужно писать только большими буквами, разделяя отдельные слова нижним подчёркиванием

Хорошо

enum class MyEnum {
    MY_CONST_1,
    MY_CONST_2,
    MY_CONST_3,
};

Плохо

enum class MY_ENUM {
    my_const_1,
    MyConst2,
    My_Const_3,
};

5.5. Пространства имён

Название пространства имён должно начинаться с маленькой буквы, отдельные слова начинаются с маленькой буквы и отделяются символом _

Хорошо

namespace my_namespace { }

Плохо

namespace My_Namespace { }
namespace MyNamespace { }
namespace MY_NAMESPACE { }