Форматирование кода
Файл настроек AStyle для форматирования кода
--suffix=none
--exclude=.svn --exclude=(Documentation) --exclude=Externals --ignore-exclude-errors
--style=allman
--indent=spaces=2
--indent-switches
--indent-preproc-block
--indent-col1-comments
--min-conditional-indent=0
--max-continuation-indent=80
--break-blocks
--pad-oper
--pad-header
--unpad-paren
--align-pointer=middle
--align-reference=middle
--add-braces
--keep-one-line-blocks
--close-templates
--max-code-length=80
--break-after-logical
--lineend=windows
Правила именования идентификаторов
class ClassName
class IInterfaceName
template<class TTemplateParam>
using Name_t = ...
ClassFunctionName
GlobalFunctionName
IsFunctionReturnBool
(*fnCallBackFunction)
m_ClassMemberVariable
_FunctionParamVariable
LocalVariable
IsBoolVariable
pPointerName
itIterator(ritReverseIterator)
iIndexValue
MemberEnum
Constant::ConstantVariable
FUCKING_DEFINE_MACROS
Общие
- Использовать RTTI исключительно в тестах или для логирования работы программы.
- В качестве целых типов использовать типы, определенные в <cstdint> и size_t.
- Не использовать беззнаковые типы данных, такие как uint32_t (за исключением маски), например, чтобы показать что значение переменной не может быть отрицательным. Для этой цели использовать assert’ы.
- Использовать assert(... && "message");
- Использовать макросы только в случае крайней необходимости, делать #undef сразу после использования.
- Директивы препроцессора должны иметь отступ после #.
- Для условной компиляции (debug и release версий, например) использовать не директивы компилятора, а "const bool debug = true;".
- Вместо массивов и оператора new[] использовать std::vector.
- Хранить в контейнерах stl не объекты, а указатели или shared_ptr'ы.
- Использовать постинкремент (i++) только для простых типов, для итераторов и шаблонных типов - ++it.
- Объявлять не изменяющиеся переменные как константы (даже в параметрах функций).
- Все enum'ы оборачивать в класс (пример).
- Интерфейсный класс должен содержать только чисто виртуальные функции и статические методы.
- Множественное наследование допустимо только от интерфейсных классов.
- Открытые функции (интерфейс класса) лучше делать не виртуальными, а виртуальными - закрытые или защищенные (паттерн NVI).
- В открытых функциях классов использовать проверку параметров с бросанием исключений, в закрытых - assert’ы.
- Чрезмерное использование встроенных функций может сделать программу медленней. На современных процессорах меньший код обычно исполняется быстрее из-за лучшего использования командного кэша.
- Статические переменные класса всегда "заворачивать" в функции.
- Не константные статические переменные нельзя использовать в многопоточном коде.
- Переменные класса должны быть закрытыми и функций доступа к ним не должно быть вообще или она должна быть только одна (SetValue() или GetValue()).
- При передаче в функцию nullptr, boolean или символьных целых значений следует их комментировать или писать самодокументирующийся код используя константы (пример).
- Исключения не должны пересекать границы модулей/подсистем.
- Комментирование кода производить в стиле Doxygen.
Заголовочные файлы
- Заголовочные файлы должны иметь расширение .hpp.
- В начале заголовочного файла ставить "#pragma once".
- Не использовать #include, когда достаточно предварительного объявления.
- Определение класса должно начинаться с открытых функций, затем должны идти защищенные функции и переменные, затем - закрытые. После закрытых функций должны быть объявлены конструкторы и деструкторы.
- Объявления дружественных тестовых функций должны находиться в самом конце объявления класса.
- В объявлениях функций без параметров всегда явным образом писать void (чтобы визуально отличать объявление функции от ее вызова).
- В каждом заголовочном файле должна быть только одна сущность (класс, общий перечислитель, константы и т.д.).
Файлы определений
- Файлы определений должны иметь расширение .cpp (файл определения шаблонных и inline-функций - .inl).
- Порядок следования заголовочных файлов: библиотеки C, библиотеки C++, .hpp файлы других библиотек, .hpp файлы текущего проекта.
- Определение вложенного класса должно находиться в отдельном .cpp файле.
- В .cpp файле первыми должны идти конструкторы и деструктор, затем все остальные функции в порядке их объявления.
- В определении функции, объявленной как override (virtual, static) ставить
int ClassName::GetValue(void)
{
}
void ClassName::DoProcess(void)
{
}
- В определении функции сначала ставить входные, затем выходные параметры.
- Параметры, являющиеся результатом работы функции должны передаваться через указатели(ссылки - только константные!).
- Параметры, являющиеся результатом работы функции, должны быть помечены комментарием out
void GetValue( int * _pValue);
Правила именования переменных/функций/классов/файлов
- Имена переменных должны быть существительными, функций - глаголами/командами.
- Имена файлов должны совпадать с именами объявленных в них классов (включая пространство имен).
- Правила именования идентификаторов.
Пространства имен
- Каждый отдельный модуль программы должен иметь свое пространство имен.
- Не использовать директиву using namespace вне функций.
- Пространство имен должно завершаться комментарием "} // namespace <name>".
- Код внутри пространства имен должен идти по левому краю.
- Вместо статических констант использовать константы в не именованных пространствах имен (использовать их в заголовочных файлах только для объявления констант!).
Конструкторы/деструкторы
- Всегда явным образом определять конструктор по умолчанию, инициирующий внутренние переменные класса.
- Использовать ключевое слово explicit для конструкторов с одним аргументом.
- Если класс не имеет явного конструктора копирования, наследовать его от boost::noncopyable.
- Деструктору всегда явным образом указывать noexcept.
Примеры
Пример 1: Перечисления
class Status
{
public:
enum Value : size_t
{
Unknown = 0,
Play,
Pause,
Stop,
Illegal
};
};
Status::Value CurrentStatatus = Status::Stop;
Пример 2: Константы
namespace
{
template<class T>
class Constant final
{
public:
static constexpr T PI = static_cast<T>(math::PI);
};
}
auto DoublePI = 2.0f * Constant<float>::PI;
Пример 3: Именование параметров
const bool IsSsuccess = CalculateSomething(InterestingValue,
10,
false,
NULL);
вместо:
const bool IsSuccess = CalculateSomething(InterestingValue,
10,
false,
nullptr);
Или, в качестве альтернативы, используя константы или само-описывающие переменные:
const int DefaultBaseValue = 10;
const bool IsFirstTimeCalling = false;
Callback * fnNullCallback = nullptr;
const bool IsSuccess = CalculateSomething(InterestingValue,
DefaultBaseValue,
IsFirstTimeCalling,
fnNullCallback);
Под вопросом
- Запятые в списке инициализации переменных в конструкторе ставить после переменной.
- Вместо <iostream> в заголовочных файлах писать <iosfwd> для ускорения компиляции.
- Для быстрого копирования потоков ввода - вывода(из одного в другой), использовать функцию потока .rdbuf() (cout << file.rdbuf()).
- С помощью функции std::uncaught_exception можно проверить, как завершает работу функция: это нормальный возврат или было выброшено исключение.
- Преобразовывайте тип числа с плавающей точкой в целочисленный тип исключительно через функции стандартной библиотеки(«round», «floor», «ceil» и т.п.)
- Для инициализации переменных использовать списки инициализации (они не позволяют сужение типов: int x1 = { 7.3 }; //compilation error).
- Профайлер не может подсказать, какие inline функции не должны быть встраиваемыми!
- Строки хранить в отдельном файле (лучше.cpp).
- Исключение не должно покидать деструктор (особенно это касается работы с контейнерами STL).
- Для класса предоставлять функцию swap() (она должна быть бессбойной!), которую можно использовать для присваивания со строгими гарантиями безопасности (для примитивных типов использовать std::swap())
T & T::operator= (T Temp)
{
swap(Temp);
return *this;
}
- Для отсортированного набора объектов можно использовать индексные контейнеры, содержащие итераторы на основной контейнер.
- Для добавления элементов в контейнер предпочтительнее использовать операции с диапазонами.
- container<T>(c).swap(c); // Удаление всей лишней (зарезервированной) емкости контейнера.
- container<T>().swap(c); // Удаление всех элементов и очистка памяти контейнера.
- find / find_if и count / count_if - поиск в неотсортированном диапазоне.
- lower_bound, upper_bound, equal_range(реже binary_search) - поиск в отсортированном диапазоне.
- Функциональные объекты должны содержать константный operator().
- Побочные подзадачи выделять в отдельный код.