Предисловие переводчика
Десять лет назад появилась первая редакция High Integrity C++ Coding Standard. К тому времени С++ был уже пять лет как стандартизирован ISO и почти двадцать лет использовался в индустрии. Это был соверешенно взрослый язык с широким сообществом профессиональных и не только разработчиков. Сообщество это накопило достаточно анекдотов про расстрелянные ноги, чтобы в нем созрела идея о создании стандарта ограничений — перечня того, чего в языке делать не следует.
К тому времени уже существовали подобные стандарты, но они как правило были закрыты и малоизвестны. HICPP, изданный компанией Programming Research Ltd, стал первым открытым стандартом высококачественного программирования на языке С++. Чуть позже к нему присоединились JSF AV C++, открытый компанией Локхид-Мартин и MISRA-C++:2008 Американской ассоциации надежности автомобильной индустрии. Из этой тройки HICPP остается наиболее популярным среди широкой публики.
Конструктивно HICPP — это примерно две сотни правил и рекомендаций, каждое из которых позволяет избежать глупых ошибок при использовании того или иного аспекта языка. К каждому правилу прилагается пояснение, иногда сопровождаемое примером кода, список исключений и ссылка на тот или иной источник. Все вместе — 84 страницы мозгодробительного текста, с которым крайне желательно ознакомиться всякому, кто не хочет переизобрести все эти правила одно за одним на основе личного печального опыта.
Данный перевод предназначается исключительно для поверхностного ознакомления и содержит только текст самих правил без пояснений и примеров. Полный текст стандарта на языке оригинала доступен по адресу: http://www.codingstandard.com/HICPPCM/
Общие правила
Правило 2.1 Все отклонения от правил стандарта должны быть подробно прокомментированы в коде.
Рекомендация 2.2 Компилятор стоит настроить так, чтобы «char» реализовывался как «unsigned char».
Классы: общие правила
Правило 3.1.1 Члены класса следует определять в порядке, соответствующем уровню доступа: сначала «public», потом «protected», последними «private».
Правило 3.1.2 Экземпляры класса стоит определять напрямую конструктором, а не копирующим конструктором через присвоение.
Правило 3.1.3 Для классов, которые управляют ресурсами, необходимо определить конструтор копирования, оператор присвоения и деструктор.
Правило 3.1.4 Для определения конструктора присвоения используйте атомарную не генерирующую исключения операцию «swap».
Правило 3.1.5 Оператор присвоения должен быть реализован корректно относительно самоприсвоения, наследования, управления ресурсами и консистентно относительно присвоения по умолчанию.
Правило 3.1.6 Не объявляйте виртуальные функции инлайнами.
Рекомендация 3.1.7 Не объявляйте методы класса инлайнами, вместо этого определяйте их в теле класса.
Правило 3.1.8 Все методы, которые не изменяют видимое извне состояние объекта, должны быть определены константными.
Рекомендация 3.1.9 Каждое конкретное поведение должно быть определено только в одном методе класса.
Правило 3.1.10 Не определяйте операторы конвертации к фундаментальным типам.
Правило 3.1.11 Не определяйте операторы конвертации к классам.
Рекомендация 3.1.12 Обеспечьте оператор вывода в поток "<<" для каждого класса.
Рекомендация 3.1.13 Все классы обязаны предоставлять минимальный стандартный интерфейс: конструктор по умолчанию, копирующий конструктор, оператор присвоения, деструктор (за исключением конструтора по умолчанию, который генерируется неявно при отстутствии любого другого явно определенного конструктора).
Классы: конструкторы и деструкторы
Правило 3.2.1 Все конструкторы должны предоставлять начальное значение (или вызывать в свою очередь конструкторы) для каждого виртуального базового класса, каждого невиртуального базового класса и каждого нестатического члена.
Правило 3.2.2 Записывайте члены класса в списке инициализации в том же порядке, в каком они объявляются.
Правило 3.2.3 Определяйте все конструкторы с одним аргументом как «explicit» (за исключением копирующего конструктора).
Рекомендация 3.2.4 Абстрактный класс не должен иметь публичных конструкторов.
Правило 3.2.5 Деструкторы должны освобождать все ресурсы, принадлежащие объекту.
Рекомендация 3.2.6 Не объявляйте конструкторы и деструкторы инлайнами.
Классы: наследование
Правило 3.3.1 Деривация должна быть исключительно публичной.
Правило 3.3.2 Определяйте виртуальные деструкторы для базовых классов.
Правило 3.3.3 Избегайте приведения указателей базового класса к указателям наследованного.
Правило 3.3.4 Избегайте приведения к виртуальному базовому классу, это необратимо (для приведения вниз по иерархии допустимо использовать «dynamic_cast», но это требует RTTI и сопряжено соответствующим оверхедом).
Правило 3.3.5 Переопределяйте все перегрузки виртуальных методов базового класса.
Правило 3.3.6 Если вирутальная функция базового класса не переопределена ни в одном наследующем классе, она не должна быть виртуальной.
Правило 3.3.7 Определяйте виртуальные функции в базовом классе только так, чтобы их поведение было корректным поведением по умолчанию для любого наследующего класса (но деструкторы должны быть определены всегда).
Правило 3.3.8 Если каждый наследующий класс предоставляет собственную уникальную реализацию виртуальной функции базового класса, то функция базового класса должна быть чистой вирутальной.
Правило 3.3.9 Если все наследующие классы предоставляют одинаковую реализацию виртуальной функции базового класса, она должна быть не виртуальной, а реализовывать поведение наследующих классов.
Правило 3.3.10 Убедитесь, что возвращаемый тип переопределенной виртуальной функции совместим с возвращаемым типом виртуальной функции.
Правило 3.3.11 Не переопределяйте и не скрывайте невиртуальные функции наследуемого класса.
Правило 3.3.12 При переопределении и переобъявлении функций, используйте одинаковые параметры по умолчанию во всех определениях.
Правило 3.3.13 Не используйте виртуальные методы объявляемого класса в его же конструкторе и деструкторе.
Правило 3.3.14 В абстрактном классе определяйте оператор присвоения как «protected».
Правило 3.3.15 Убедитесь, что базовые классы, общие для двух и более наследующих классов, являются виртуальными.
Правило 3.3.16 Полиморфные методы в наследующем классе явно определяйте виртуальными.
Классы: ООП
Правило 3.4.1 Объявляйте данные класса «private».
Правило 3.4.2 Не возвращайте неконстантные ссылки на члены класса из константных функций.
Правило 3.4.3 Не определяйте методов, которые возвращают неконстантные указатели и ссылки на данные с более ограниченным доступом, чем сам метод.
Правило 3.4.4 Избегайте использования дружественных классов и методов, если только архитектура не требует их явно.
Правило 3.4.5 Класс, предназначенный для публичного наследования, должен быть абстрактным.
Правило 3.4.6 Наследующие классы должны наследовать не более чем от одного не чистого абстрактного класса.
Рекомендация 3.4.7 Все члены публичного базового класса должны оставаться валидными для наследующего.
Классы: перегрузка операторов
Правило 3.5.1 Избегайте перегрузки операторов ",", "&&" и "||".
Правило 3.5.2 Всегда реализуйте перегрузку операторов так, чтобы эквивалентные операции оставались эквивалентными после перезагрузки.
Правило 3.5.3 Убедитесь, что перегруженные бинарные операторы сохраняют ожидаемое поведение.
Правило 3.5.4 Объявляйте перегруженные операторы вне класса, чтобы для них работала конвенция приведения по левому операнду.
Правило 3.5.5 При перегрузке оператора индекса "[]", реализуйте и константную, и неконстантную версии.
Сложность
Правило 4.1 Функции не должны иметь избыточную цикломатическую сложностью по Маккабе (>10).
Правило 4.2 Функции не должны иметь излишне большое число статических путей выполнения (>200).
Правило 4.3 Функции не должны иметь большое число аргументов (>6).
Поток управления
Правило 5.1 Каждый блок примитива управления потоком («if», «else», «while», «for», «do», «switch») должен заключаться в скобки, даже если он содержит только одну строку, или не содержит ничего вовсе.
Правило 5.2 В булевских выражениях («if», «for», «while», «do» и первом операнде тернарного оператора "?") всегда записывайте равенство и неравенство в явном виде.
Правило 5.3 Избегайте условных выражений, которые всегда имеют один и тот же результат (за исключением платформозависимого кода, когда результат на другой платформе может все-таки быть иным).
Правило 5.4 Каждый непустой блок выражения «switch» должен заканчиваться командой «break».
Правило 5.5 Не модифицируйте переменные контроля цикла «for» в теле цикла.
Правило 5.6 Не модифицируйте переменные контроля цикла «while» и «do» в теле цикла более чем один раз.
Правило 5.7 Переменная контроля цикла «for» должна сравниваться с константой, а не функцией или выражением.
Правило 5.8 Не используйте «goto».
Правило 5.9 Каждый законченный блок, кроме тела «switch» должен иметь одну точку входа и (исключая выход по исключению), одну точку выхода.
Правило 5.10 Все точки выхода функций с не пустым типом возврата должны возвращать выражение типа возврата.
Правило 5.11 Определяйте в явном виде все возможные ветви условного оператора.
Правило 5.12 Переменные контроля цикла «for» следует объявлять непосредственно внутри «for», а не использовать внешние переменные.
Константы
Правило 6.1 Используйте суффиксы «L», «U», «UL» для констант типа «unsigned», «long», «unsigned long».
Правило 6.2 Используйте суффиксы «F», «L» для констант типа «float», «long double».
Правило 6.3 Следите, чтобы константа определялась значением не выходящим за область определения типа.
Правило 6.4 Используйте ескейп-последовательности исключительно стандарта ISO C++.
Правило 6.5 Не смешивайте литералы обычной строки с литералами строки широких символов.
Рекомендация 6.6 Глобальные переменные и статические члены следует делать константными (ислючая переменную контроля уникальности синглтона).
Конверсия и приведение
Правило 7.1 Используйте только приведение через «static_cast», «dynamic_cast», «const_cast», «reinterpret_cast» или явное приведение через конструктор целевого типа
Рекомендация 7.2 Минимизируйте число приведений.
Правило 7.3 Избегайте приведений из «volatile» в не «volatile».
Правило 7.4 Избегайте приведений из «const» в не «const».
Правило 7.5 Избегайте приведений ссылок и указателей (за исключением приведения через dynamic_cast при поддержке RTTI).
Правило 7.6 Конвертируйте тип числа с плавающей точкой в целочисленный тип исключительно через функции стандартной библиотеки («round», «floor», «ceil» и т. п.)
Правило 7.7 Не приводите указатели к базовым типам.
Правило 7.8 Не допускайте неявного приведения аргументов функции при ее вызове.
Объявления и определения: структура
Рекомендация 8.1.1 За исключением определений объектов, а также объявлений и определений неименованного пространства имен, все функции, классы, переменные, перечисления и переменные перечислений, имеющие внешнюю компоновку и определенные в некотором (включая глобальное) пространство имен, должны быть определены в заголовочных файлах.
Рекомендация 8.1.2 За исключением объявлений и определений неименованного пространства имен, все инлайн-функции, имеющие внешнюю компоновку и определенные в некотором (включая глобальное) пространство имен, должны быть определены в заголовочных файлах.
Рекомендация 8.1.3 За исключением объявлений и определений неименованного пространства имен, все шаблонные определения, имеющие внешнюю компоновку и определенные в некотором (включая глобальное) пространство имен, должны быть определены в заголовочных файлах.
Объявления и определения: видимость
Правило 8.2.1 Не затеняйте объявления в внутренних областях видимости.
Правило 8.2.2 Избегайте использования глобальных переменных.
Рекомендация 8.2.3 Используйте декларации «using», или обозначайте пространство имен явно, избегайте директив «using».
Рекомендация 8.2.4 Директивы «using» допускается использовать только в главном исходном файле и только после всех директив «include».
Объявления и определения: языковые ограничения
Правило 8.3.1 Избегайте объявления «static» объектов внутри пространства имен.
Рекомендация 8.3.2 Избегайте избыточного использования «extern» там, где компоновка и так неявно объявлена внешней.
Правило 8.3.3 Избегайте использования «auto» и «register».
Правило 8.3.4 Все идентификаторы должны быть уникальны.
Правило 8.3.5 Избегайте неоднозначной грамматики декларации и приведений с помощью функций.
Объявления и определения: общие правила
Правило 8.4.1 Не используйте пары символов «l» (буква «эль») и «1» (один), а также «O» (буква «о») и «0» (ноль) в одном идентификаторе.
Правило 8.4.2 Объявляете каждую переменную в отдельной строке отдельным объявлением. Если объявление не самоочевидно, добавляйте соответствующий комментарий.
Правило 8.4.3 Инициализируйте каждый объект при определении. Никогда не используйте объект до первого присвоения ему значения.
Правило 8.4.4 Определяйте переменные как можно ближе к контексту использования.
Правило 8.4.5 Не используйте тип «char» при объявлении объектов, участвующих в численных операциях. Используйте явное определение знаковости типа: «signed char», «unsigned char».
Рекомендация 8.4.6 Используйте типы классов или определения типов через «typedef» для обособления типа числа.
Правило 8.4.7 Объявляйте только одно имя в каждом «typedef».
Правило 8.4.8 Не определяйте типы массивов через «typedef».
Правило 8.4.9 Не используйте агрегатные типы без проверки переполнения (массивы языка Си).
Правило 8.4.10 Не используйте указатели на члены.
Правило 8.4.11 Используйте «const» везде, где это возможно.
Рекомендация 8.4.12 Присоединяйте "*" и "&" к типу (а не к имени переменных) в объявлениях и определениях.
Рекомендация 8.4.13 Предпочитайте знаковые типы беззнаковым.
Исключения
Правило 9.1 Не генерируйте исключения из деструкторов.
Правило 9.2 Генерируйте исключения только объектного типа (от std::exception).
Правило 9.3 Обрабатывайте исключения по ссылке.
Рекомендация 9.4 Используйте механизм обработки исключений С++ только для регулирования нештатных ситуаций.
Рекомендация 9.5 Каждое приложение должно гарантировать освобождение всех ресурсов (исключая локальные объекты) при генерации исключения.
Рекомендация 9.6 Каждое приложение должно гарантировать освобождение всех не автоматически освобождаемых ресурсов при неожиданном завершении программы.
Выражения
Правило 10.1 Используйте символические имена вместо конкретных значений в коде. Избегайте «magic numbers» (за исключением "", 0, 1, true, false и однократных строковых литералов).
Правило 10.2 Доступ к массиву должен очевидно выполняться в рамках его границ.
Правило 10.3 Не полагайтесь на определенный порядок вычисления операндов выражения.
Правило 10.4 Используйте скобки дополнительно для раскрытия сути выражения.
Правило 10.5 Всегда игнорируйте результат оператора присвоения.
Рекомендация 10.6 При сравнении переменной и константы всегда размещайте константу слева.
Рекомендация 10.7 Избегайте выражений, которые зависят от неявной конверсии операндов.
Правило 10.8 Выражения, используемые в утверждениях («assert»), должны быть свободны от сторонних эффектов.
Правило 10.9 Не допускайте сторонних эффектов в правой части операторов "&&", "||", а также в операндах «sizeof» и «typeid».
Правило 10.10 Избегайте инструкций без сторонних эффектов.
Правило 10.11 Не используйте следующие операторы со знаковыми операндами: "<<", ">>", "&", "|", "^".
Правило 10.12 Валидируйте аргументы операторов сдвига.
Правило 10.13 Не используйте знаковые и беззнаковые типы в одном выражении.
Правило 10.14 Не используйте типы с разной арифметической точностью в одном выражении.
Правило 10.15 Не полагайтесь на точные значения чисел с плавающей точкой.
Правило 10.16 Не используйте оператор инкремента на переменной булевского типа.
Правило 10.17 Проверяйте на ноль операнды делителей при делении и взятии остатка.
Рекомендация 10.18 Проверяйте на неотрицательность операнды целочисленного деления.
Правило 10.19 Избегайте оператора запятой.
Правило 10.20 Избегайте тернарного оператора в выражениях.
Правило 10.21 Используйте унарный минус только со знаковыми операндами.
Функции
Правило 11.1 Все одноименные функции должны иметь одинаковое поведение, различаясь только числом и типом аргументов.
Правило 11.2 Закрывайте все функции не члены, которые не являются частью внешнего инетрфейса в неименованном пространстве имен, в исходных файлах.
Правило 11.3 Обозначайте имя аргументов функции как при декларации, так и при имплементации (за исключением имен переменных, которые в имплементации не используются). Используйте одинаковые имена в обоих случаях.
Правило 11.4 Предпочитайте передачу по ссылке передаче по значению и по указателю.
Правило 11.5 Передавайте аргументы, предназначенные только для чтения, по ссылке на константу. Переменные фундаментальных типов допустимо передавать по значению.
Правило 11.6 Не используйте многоточия в списке аргументов функции.
Правило 11.7 Функция не должна возвращать указатель на автоматически аллоцируемую переменную, объявленную в теле функции. Вместо этого следует вернуть копию объекта.
Правило 11.8 Объявляйте как inline только тривиальные функции.
Правило 11.9 Не допускайте перегрузки функции для численного типа и для указателя одновременно.
Управление памятью
Правило 12.1 Не задавайте значения по умолчанию для аргументов перегруженных функций.
Правило 12.2 Аллоцируйте объекты в памяти с помощью «new» и деаллоцируйте с помощью «delete». Не используйте функции управления памятью языка Си: «malloc», «realloc», «free».
Правило 12.3 Соблюдайте соответствие «new» — «delete» и «new[]» — «delete[]».
Правило 12.4 При удалении массива объектов указывать количество элементов не следует.
Правило 12.5 Не возвращайте разыменованный указатель, если он инициализирован динамически в теле функции.
Правило 12.6 При переопределении «new», всегда переопределяйте «delete».
Правило 12.7 Операторы «new» и «delete» стоит переопределять статическими явно.
Правило 12.8 После деаллокации объекта через «delete» всегда устанавливайте указатель нулевым.
Переносимость
Рекомендация 13.1 Избегайте нестандартизированного поведения.
Рекомендация 13.2 Предпочитайте стандартные средства языка и стандартной библиотеки дополнительной функциональности предоставляемой операционной системой или окружением.
Правило 13.3 Не превышайте ограничения транслятора, установленные стандартом ISO C++.
Правило 13.4 Не используйте расширения компилятора или препроцессора.
Правило 13.5 Избегайте встроенного ассемблерного кода.
Правило 13.6 Не полагайтесь на структуру внутреннего представления объектов и переменных.
Правило 13.7 Не приводите указатель на переменную фундаментального типа к указателю на переменную фундаментального типа с более строгим выравниванием.
Препроцессор
Правило 14.1 Используйте комментарии языка С++ "// ...", избегайте комментариев в стиле Си "/*… */".
Рекомендация 14.2 Не используйте табуляцию в исходных файлах.
Рекомендация 14.3 Записывайте директивы препроцессора с начала строки без пробелов между "#" и собственно директивой.
Рекомендация 14.4 Записывайте вложенные директивы сопроцессора с начала строки, но с пробелами между "#" и вложенной директивой.
Правило 14.5 Управляйте условной компиляцией исключительно наличием или отсутствием определения того или иного токена (при помощи "#ifdef", "#ifnded". Исключение составляют случаи, когда сравнение с константой неизбежно. Например, при компиляции кода компилятором определенной версии).
Правило 14.6 Для определения различия между компиляцией Си и С++ используйте идентификатор "__cplusplus".
Рекомендация 14.7 Избегайте комментариев в теле макроса.
Правило 14.8 Все файлы, содержащие исходный код должны заканчиваться пустой строкой (точнее символом новой строки по ISO C++ 2.1/2).
Правило 14.9 Используйте скобки "<>" для включения заголовочных файлов системных и стандартных библиотек, а кавычки — для всех остальных.
Правило 14.10 Избегайте явного указания путей в директивах "#include".
Правило 14.11 Используйте гарды в заголовочных файлах во избежание их повторного включения.
Правило 14.12 Используйте строчные буквы для имен файлов и ссылок на имена файлов (как в директивах include).
Правило 14.13 Составляйте заголовочные файлы так, чтобы все необходимое для их компиляции включалось в самих файлах.
Правило 14.14 Заключайте в скобки как аргументы, так и тело макроса (за исключением односложных тел).
Правило 14.15 Не используйте макросы препроцессора для определения сегментов кода.
Правило 14.16 Избегайте использования макроса NULL.
Правило 14.17 Используйте константные объекты или перечисления для определения констант, не "#define".
Правило 14.18 Не используйте диграфы и триграфы.
Правило 14.19 Не используйте макросы для определения функций, используйте инлайн-функции.
Структуры, объединения, перечисления
Правило 15.1 Избегайте использования вариативных структур (объединений).
Правило 15.2 Не определяйте функции-члены и не используйте модификаторы доступа в структурах.
Правило 15.3 Не полагайтесь на численное значение переменной перечисления
Правило 15.4 Избегайте приведения целочисленного типа к перечислению: результат непредсказуем, если значение переменной не входит в область определения перечисления.
Темплейты
Правило 16.1 Избегайте неявного приведения темплейтных классов к независимым типам.
Правило 16.2 Избегайте потенциально конфликтующих методов в темплейтных классах.
Правило 16.3 Инстанцируйте темплейты исключительно с аргументами темплейта, которые удовлетворяют требованиям интерфейса.
Рекомендация 16.4 Прибегайте к темплейтам только тогда, когда поведение класса или функции полностью не зависит от типа объекта темплейтного аргумента.
STL
Правило 17.1 Используйте стандартные заголовочные файлы С++, определенные стандартом языка, а не устаревшие в расширением ".h" ("", не "<sdtio.h>").
Правило 17.2 Предпочитайте контейнеры и алгоритмы STL самодельным аналогам.
Правило 17.3 Для объектов, предназначенных для использования в контейнерах, копирование должно быть реализовано с максимальной эффективностью.
Правило 17.4 Если копирование объекта ресурсоемкое, используйте контейнер указателей или смарт-поинтеров.
Правило 17.5 Не вставляйте объект наследованного класса в контейнер объектов базового класса (такое работает только с контейнером указателей).
Рекомендация 17.6 Используйте «empty» вместо проверки «size» на 0.
Рекомендация 17.7 Не используйте контейнеры STL в роли публичных базовых классов.
Правило 17.8 Никогда не создавайте контейнеров типа auto_ptr.
Правило 17.9 Используйте строки и вектора вместо динамически аллоцируемых массивов.
Рекомендация 17.10 При возможности преаллоцируйте контейнер («reserve») заранее, чтобы избежать повторных реаллокаций в дальнейшем.
Правило 17.11 При передаче векторов функциям языка Си, используйте конструкцию "&v[0]".
Правило 17.12 Для использования строки STL в виде «char*» с устаревшими функциями используйте исключительно «c_str()».
Правило 17.13 Избегайте векторов типа «bool».
Правило 17.14 Предикаты отношения должны возвращать «false» для эквивалентных значений.
Правило 17.15 Никогда не модифицируйте ключевую часть элемента множества типа «set» или «multiset».
Рекомендация 17.16 Избегайте смешения типов итераторов.
Правило 17.17 Результат предиката должен зависеть исключительно от его параметров.
Правило 17.18 Предпочитайте алгоритмы STL собственным циклам.
Правило 17.19 Предпочитайте функции-члены контейнеров одноименным обобщенным алгоритмам.
Правило 17.20 Включайте только необходимые заголовочные файлы STL.
Рекомендация 17.21 Избегайте использования auto_ptr.
Список литературы
[Stroustrup, 2000] Bjarne Stroustrup: The C++ Programming Language. Addison-Wesley. 2000
[C++ Standard, 1999] International Standard ISO/IEC 14882:1998(E) Programming Language C++.
[Effective C++, 1996] Scott Meyers: Effective C++. Addison-Wesley. 1996
[More Effective C++, 1996] Scott Meyers: More Effective C++. Addison-Wesley. 1996
[Effective STL, 2001] Scott Meyers: Effective STL. Addison-Wesley. 2001
[Industrial Strength C++, 1997] Mats Henricson, Erik Nyquist, Ellemtel Utvecklings AB: Industrial Strength C++. Prentice Hall. 1997
[Exceptional C++, 2000] Herb Sutter: Exceptional C++. Addison-Wesley. 2000
Автор: akalenuk