Основано на руководстве от google, но не идентично ему.
Желательно, чтобы каждый .cc файл исходного кода имел парный .h заголовочный файл. Также есть известные исключения из этого правила, такие как юниттесты или небольшие .cc файлы, содержащие только функцию main().
Заголовочные файлы должны быть самодостаточными (в плане компиляции): иметь блокировку от повторного включения и включать все необходимые файлы.
Заголовочные файлы должны иметь суффикс .h. Другие файлы (не заголовочные), предназначенные для включения в код, должны быть с суффиксом .inc и могут не иметь блокировки от повторного включения.
Все заголовочные файлы должны иметь защиту от повторного включения посредством #define, а не #pragma once. Формат макроопределения должен быть: <PROJECT>_<PATH>_<FILE>_H.
#ifndef FOO_BAR_BAZ_H #define FOO_BAR_BAZ_H ... #endif // FOO_BAR_BAZ_H
Не полагайся на вложенные подключения заголовочных файлов. Это позволит удалить неиспользуемые #include, сохраняя при этом корректность остального кода. Это правило актуально даже для парных файлов: если, например, foo.h включает bar.h, но foo.cc также использует определения из bar.h, то foo.cc должен явно подключать bar.h сам, независимо от того, что foo.h уже его включает.
Использование встраиваемых функций может генерировать более эффективный код, особенно когда функции маленькие. Используй эту возможность для get/set функций, других коротких и критичных для производительности функций.
Не стоит делать функции встраиваемыми, если они превышают 10 строк кода. Избегай делать встраиваемыми деструкторы, тк они неявно могут содержать много дополнительного кода: вызовы деструкторов переменных и базовых классов! Обычно нет смысла делать встраиваемыми функции, в которых есть циклы или операции switch (кроме вырожденных случаев, когда цикл или другие операторы никогда не выполняются).
Включай заголовочные файлы в следующем порядке:
Отделяй каждую (непустую) группу файлов пустой строкой.
В подключении не стоит использовать UNIX псевдонимы (. и ..).
При подключении заголовочных файлов используй угловые скобки только если это требуется библиотекой. В частности:
Размещай свой код в namespace (с уникальным именем).
Не используй директиву using, что бы избежать потенциальных конфликтов (коллизий) имен.
В конце объявления многострочного пространства имён добавляй комментарий с именем этого пространства имен.
Конструктор не должен вызывать виртуальные функции:
bool isValid().
Используйте Фибричный Метод ( ссылка!) или метод init() Используйте
init() только в случае, если у объекта есть флаги состояния, разрешающие вызывать те или иные публичные функции (тк сложно полноценно работать с частично сконструированным объектом).
Не объявляй неявные преобразования. Используй ключевое слово explicit для операторов преобразования типа и конструкторов с одним аргументом.
Желательно писать маленькие и сфокусированные на одной задаче функции. Если ф-я превышает 40 строк, стоит задуматься о разбитии.
Перегружайте функцию, если нет семантических различий между её вариантами. В этом случае допустимо изменять как типы аргументов, так и квалификаторы (const и др.) или количество аргументов. Делайте перегрузку так, чтобы не было нужды разбираться, какая именно версия функции будет вызвана.
Аргументы по-умолчанию под запретом для виртуальных функций (где они могут работать некорректно) и для случаев, когда значение для аргумента может измениться. Например, не пишите такой код: void f(int n = counter++);.
В C++ есть две формы декларации функций. В старой форме возвращаемый тип указывается перед именем функции:
int foo(int x);
Новая форма использует auto перед именем функции и завершается возвращаемым типом, указываемым после списка аргументов. Например, предыдущую декларацию можно записать как:
auto foo(int x) -> int;
Используйте обычный (более старый) стиль декларации функции, когда возвращаемый тип указывается перед именем функции. Новую же форму (возвращаемый тип в конце) используйте либо по явной необходимости (лямбды), либо для улучшения читабельности кода. Причём последний вариант (читабельность) часто свидетельствует о чересчур сложных шаблонах, лучше их избегать.
| case | type | пример |
|---|---|---|
| PascalCase | class | UserAccount |
| struct | Point | |
| typedef | StringList | |
| using | IntPtr | |
| enum | Color | |
| camelCase | функции | calculateTotal |
| методы классов | addUser | |
| accessor-ы, get | getUserName | |
| mutator-ы, set | setUserEmail | |
| snake_case | переменные | user_age |
| параметры | file_path | |
| члены class (*_) | account_balance_ | |
| члены struct | coordinates | |
| namespace | my_utilities | |
| UPPER_SNAKE_CASE | макросы | MAX_BUFFER_SIZE |
| const | DEFAULT_TIMEOUT | |
| constexpr | PI | |
| члены enum | RED |
Если файл с кодом (например .h файл) объявляет несколько абстракций для «внешнего» использования (общие функции, связанные классы и тп), то добавляйте комментарий в шапку, описывающий эту коллекцию абстракций. Не размещайте в этом комментарии детальное описание отдельных абстракций.
// Перебор содержимого GargantuanTable. // Пример: // std::unique_ptr<GargantuanTableIterator> iter = table->NewIterator(); // for (iter->Seek("foo"); !iter->done(); iter->Next()) { // process(iter->key(), iter->value()); // } class GargantuanTableIterator { ... };