Стандарт кодирования программ на языке C++

Author / Автор: Сергей Сацкий
Publication date / Опубликовано: 08.07.2005
Version / Версия текста: 1.3

Введение

Стандарт кодирования предписывает правила оформления кода на языке программирования C++. Все правила, применимые для языка программирования С, должны применяться в коде, написанном на этом языке.

Настоящий стандарт учитывает особенности некоторых вспомогательных инструментов разработчика, используемых в проекте. Примером такого инструмента может служить пакет doxygen.

Настоящий документ является скорее справочником, чем исчерпывающим объяснением выбранных решений.

Отступления от данного стандарта допускаются, если они облегчают чтение кода.

Стандарт не является панацеей от написания плохого кода.

Стандарт практически не описывает семантические аспекты использования языка. Дальнейшие редакции могут быть расширены этими элементами.

Файлы

  • Заголовочные файлы должны иметь расширение .h, файлы с исходным текстом на С++ .срр, файлы с исходным текстом на С .с.
  • В общем случае заголовочный файл должен содержать объявление одного класса. Возможно объявление нескольких вспомогательных классов, например, предикатов. Вспомогательные классы, как правило, элементарны и имеют гораздо меньший размер, чем основные.
  • Файл .cpp должен совпадать по имени с заголовочным файлом и содержать определение объявленных в .h классов.
  • Максимальная длина строки в файле - 80 символов. В случае переноса строки используйте вертикальное выравнивание со сдвигом.
  • Максимальный размер файла - 1000 строк
  • Старайтесь ограничивать размер функции 100 строками.
  • Правила именования файлов совпадают с правилами формирования идентификаторов
  • В исходных текстах запрещается использовать специальные символы TAB и page break. Символ TAB должен быть замещен на пробелы автоматически редактором текстов. (Исключение - Makefile, где символ TAB имеет специальное значение)
  • Каждый файл имеет комментарий, размещаемый в самом начале файла, за которым идет все остальное
  • Содержимое заголовочного файла должно размещаться внутри директив условной компиляции. Например, для файла MyClass.h:

#ifndef MYCLASS_H
#define MYCLASS_H

. . .

#endif

  • Все включаемые директивой #include файлы должны находиться в начале файла
  • Включаемые файлы должны быть отсортированы и сгруппированы. Сортировка производится от низкоуровневых к высокоуровневым файлам. Для группировки используется одна или несколько пустых строк. Например:

#include <fstream>
#include <iomanip>

#include <XML/DOM.h>
#include <XML/Node.h>

#include "UI/PropertiesDialog.h"
#include "UI/MainWindow.h"

Именование

  • Для формирования идентификаторов используется английский язык с корректным написанием слов
  • Использование собственных аббревиатур в идентификаторах и сокращений сводится к минимуму. Использование общепринятых аббревиатур допускается, например: NMEA.
  • Каждое слово, входящее в состав идентификатора пишется с большой буквы. Слова пишутся слитно, например

HelloWorld

  • Для имен типов (классов, структур, перечислений) допускаются префиксы. Каждый из разработчиков может пользоваться своим собственным префиксом, например:

SMyOwnClass

  • Использование символа '_' в идентификаторах недопустимо
  • Использование зарезервированных различными международными стандартами идентификаторов не допускается
Зарезервированные идентификаторы Стандарт
Двойное подчеркивание в любом месте ISO C++, ANSI C
Одиночное подчеркивание в начале ISO C++, ANSI C
E[0-9A-Z]... ANSI C
is[a-z]... ANSI C
to[a-z]... ANSI C
LC_... ANSI C
SIG[_A-Z]... ANSI C
str[a-z]... ANSI C
mem[a-z] ANSI C
wcs[a-z]... ANSI C
..._t POSIX

  • Используйте схему

[ Префикс + ] [ Существительное | Прилагательное +] Существительное

для имен типов. Например:

class SDataHeap

  • Используйте схему

Глагол [ + Существительное ]

для имен методов и функций. Например:

void  SetNewName ( const std::string &  NewName );

  • bool переменные, как правило, должны именоваться с префиксом Is или Has, например:

bool    IsDoorClosed;

  • Имена файлов, как правило, совпадают с именами классов, содержащихся в них.
  • При именовании классов и их методов старайтесь выбирать имена таким образом, чтобы вызов вида

MyObject.Method()

Читался как имеющая смысл фраза.

  • Избегайте именования локальных объектов таким образом, что они перекрывают внешние имена
  • Если имя формального аргумента метода класса конфликтует с именем члена класса, допускается использование идентификатора формального аргумента с маленькой буквы, например:

class SAlarm
{
    public:
        SAlarm( SSeverity  severity ) : Severity( severity ) { }

    private:
        SSeverity     Severity;
};

  • Избегайте имен, незначительно отличающихся друг от друга и имен, использующих похожие символы, такие как '0', '1' и 'O', 'l'.
  • В качестве счетчиков циклов допускаются локальные однобуквенные переменные. Предпочитайте именовать их в такой последовательности: k, j, m, n, i.
  • Если макросы все-таки используются, то давайте им имена, использующие только заглавные буквы и символы подчеркивания в качестве разделителей.

Расположение текста

  • Пишите код "чанками", разделенными пустыми строками
  • Для форматирования текста используются отступы, равные 4 пробелам
  • Используйте вертикальное выравнивание
  • Блоки объявления переменных должны быть выровнены

int             SomeIntegerValue;    //!< Remark1
unsigned long   SomeLongValue;       //!< Remark2
bool            SomeBooleanValue;    //!< Remark3

Стартовая позиция типа данных, имени переменной и комментария должны попадать на позицию табулятора. После типа должно быть не менее 2 пробелов. Символы * и & относятся к типу, а не к имени. Комментарий отделяется не менее чем 2 пробелами от объявления переменной.

int *               SomeIntegerValue;   //!< Remark1
unsigned long &     SomeLongValue;      //!< Remark2
bool *              SomeBooleanValue;   //!< Remark3

  • Фигурные скобки должны быть выровнены с использованием стиля Олмэна:

void  DoSomething ( void )
{
    if ( x != y )
    {
        y = x;
    }
    else
    {
        ;    // Do nothing
    }            
}

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

  • Операторы while, for и if всегда используют блоки в фигурных скобках, даже если синтаксической необходимости нет
  • Элементы оператора switch должны быть выровнены в соответствии с позициями отступа. Альтернатива default должна присутствовать всегда. Каждая из альтернатив должна иметь break

switch ( Variable )
{
        // ...
    case 1:
        break;

        // ...
    case 2:
        break;

        // ...
    default:
        NeverGetHere;    //  See Assertion.hpp
        break;
}

  • Перенос длинных строк должен осуществляться после , ; и других операторов.
  • Операторы сравнения должны быть окружены пробелами

if ( a <= b )

  • Вслед за ключевыми словами должен идти пробел

for ( ; ; )

  • Вслед за , ; должен идти пробел. Перед этими символами пробела быть не должно.
  • Перед символами ( [ < не должно быть пробела. После них должен быть пробел.
  • Вокруг : ) ] > = должны быть пробелы. Исключение составляет вызов функции без аргументов. В этом случае допускается писать круглые скобки без пробелов (MyVector.end()).
  • Вокруг операторов . и -> пробелов быть не должно.
  • После операции разыменования (*) и взятия адреса (&) пробела быть не должно.
  • После знаков унарных операций не должно быть пробелов
  • Операторы инкрементации и декрементации должны писаться слитно с объектом, к которому они применяются
  • Символы математических операций должны быть окружены пробелами
  • Ключевое слово template со списком параметров должно располагаться на отдельной строке

template < class  EventType, class  StateType >
class SStateMachine
{
    . . .
};

  • В прототипах все аргументы должны быть именованы. После имени типа должно быть не менее 2 пробелов. В случае переноса списка на следующую строку необходимо пользоваться вертикальным выравниваем.

Комментарии

Комментарии учитывают использование системы генерации документации по исходному тексту doxygen. Для маркировки комментариев используется комбинация символов //!, /*! . . . */ и //!<. Последний означает, что комментарий относится к предшествующему элементу. Кроме того, используются комбинации @param для описания параметров функций и методов и @return для описания возвращаемых значений.

  • Комментарии даются на корректном английском языке
  • Размер комментария пропорционален сложности комментируемого объекта
  • Предпочтительный формат комментария в стиле C++: //
  • Многострочные комментарии должны быть выровнены

// First line
// Second line
// Third line

или

/* Short line                     */
/* Extended size line             */
/* Very very very very long line  */

  • В случае многострочного комментария в стиле C его предпочтительный вид такой:

/*
 *  Some remarks
 *  More remarks
 */

  • Комментарии, предшествующие комментируемому элементу, располагаются с отступом 4 пробела

    //! This is the remark
bool    SomeBooleanValue;

    //! This is the first line
    //! More detailed description
bool    SomeBooleanValue;

  • Комментарии могут даваться разными способами: предшествующие комментируемому элементу, находящиеся в той же строке, что и комментируемый элемент. Комментарий, находящийся в той же строке должен быть отделен не менее чем двумя пробелами от комментируемого элемента.

    //! This is the first type of the remark
bool    SomeBooleanValue;

bool    SomeBooleanValue;    //!< Many lines remark
                             //!< Second line of the remark

  • Каждая переменная должна быть прокомментирована
  • Каждый тип данных пользователя (класс, структура, объединение и т.п.) должен быть снабжен комментарием

    //! The SMyOwnClass is created for the special purpose
    //! And should be used everywhere it is possible
    //! With no obligation to send a post card
class SMyOwnClass
{
    . . .
};

  • Каждая функция (метод класса) должна быть снабжена комментарием

    //! The function transforms string representation into integer
    //! @param InputValue is a stringed value
    //! @param Base is a scale of notation
    //! @return transformed value of the given scale of notation
long SuperFunction ( const string &  InputValue, long  Base );

  • Каждое пространство имен должно быть снабжено комментарием
  • В начале каждого исходного файла проекта должен быть комментарий вида:

//
// File:          Template.h
//
// Creation date: July 27th 2003
//
// Author:        Sergey Satskiy
//
// Purpose:       Template for all the headers
//
// Copyright (c) 2005 Sergey Satskiy
//
// $
//

каждая строка начинается с нулевой колонки.

  • В случае, если doxygen не распознает многострочный предшествующий комментарий в стиле C++, используйте следующую форму записи:

    /*! The function transforms string representation into integer
     *  @param InputValue is a stringed value
     *  @param Base is a scale of notation
     *  @return transformed value of the given scale of notation
     */
long SuperFunction ( const string &  InputValue, long  Base );

  • Допускается использовать и другие, не показанные выше, специальные идентификаторы системы doxygen.
  • Дополнительные комментарии, данные автором помимо перечисленных выше обязательных, приветствуются.

Использование языка

  • Вместо NULL используйте 0
  • Модификатор const должен использоваться везде, где это применимо
  • Вместо определения замещений констант директивой #define используйте const.
  • В объявлении класса секции должны следовать в следующем порядке: public, protected, private. В определении класса должен использоваться тот же порядок.
  • Не используйте оператор goto
  • Избегайте глубокой вложенности операторов, блоков и структур. Предпочитайте способ обработки с простой проверкой и выходом "на бок".
  • В одном операторе объявляется одна переменная
  • Избегайте использования оператора ?
  • Избегайте использования макросов
  • Вложенные if else операторы должны быть выровнены так, как если бы они были самостоятельными операторами.
  • Инициализируйте переменные в момент объявления везде, где это возможно.
  • Конструкторы должны инициализировать по возможности все члены - данные. Инициализация должна даваться, по возможности, в списке инициализации. Помните о порядке инициализации.
  • Не забывайте делать деструкторы базовых классов виртуальными
  • Используйте контейнеры и алгоритмы STL
  • Предпочитайте явно указывать в коде вызов конструктора при инициализации значений переменных

int     Salary( 0 );    // Not int    Salary = 0;

for ( vector<int>::iterator  k( MyVector.begin() ); k != MyVector.end(); ++k )

  • Оператор присваивания должен быть один в строке. Множественные присваивания не допускаются.
  • Не используйте сравнения в математических выражениях

NumberOfDays = ( IsLeapYear() == true ) + 28;    // Not allowed

  • Не boolean результаты выражений должны использовать оператор сравнения. Не используйте неявный != 0.

if ( ObjectPointer != 0 )    // OK
if ( ObjectPointer )         // Not allowed

  • Избегайте присваиваний в операторах сравнения
  • Избегайте использования оператора , (запятая)
  • Используйте операторы приведения в стиле C++, а не преобразования по умолчанию или преобразования в стиле С
  • Используйте for ( ; ; ) для записи бесконечного цикла
  • Для обработки ошибок пользуйтесь исключениями
  • Используйте sizeof а не константы
  • Помните о методах классов, генерируемых компилятором по умолчанию
  • Установите максимальный уровень выдачи предупреждений компилятором. Добивайтесь компиляции без единого предупреждения.
  • Избегайте перегрузки по numerical и pointer типам. Это влечет неоднозначности.
  • Используйте встроенный тип bool
  • По возможности не используйте RTTI
  • Предпочитайте ссылки указателям
  • Предпочитайте long типу int
  • Предпочитайте double типу float
  • Предпочитайте префиксную форму операторов ++ и -- везде, где это возможно

Пространства имен

  • Пространства имен должны использоваться везде, где это применимо
  • Библиотечный код должен быть помещен в пространство имен
  • Библиотечный код не должен использовать директиву using namespace для нужных ему определений. Вместо этого должен быть использован оператор разрешения области видимости.

Список литературы

  1. Scott Meyers. Effective C++
  2. Scott Meyers. More Effective C++
  3. Herb Sutter. Exceptional C++
  4. CoreLinux++ coding standard
  5. Arctic Labs C++ Coding Standard
  6. Baldwin's C++ coding standard
  7. Brett Slocum C++ coding standard
  8. Geotechnical Software Services C++ Coding Standard
  9. European Laboratory for Practice Physics C++ Coding Standard
  10. SoftQuad's C++ Coding Standard
  11. Logikos's C++ Coding Standard
  12. Notes On Writing Portable Programs In C
  13. Ahntech C++ Coding Standard
  14. Object Oriented Coding Standard
  15. Programming in C++, Rules and Recommendations
  16. Quantum Coding Standard
  17. Recommended C Style and Coding Standard
  18. Automating Corporate Source Code Compliance
  19. Yongling Ding C++ Coding Standard
  20. Wildfire C++ Coding Standard


Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved.

Разрешается копирование и распространение этой статьи любым способом без внесения изменений, при условии, что это разрешение сохраняется.
Last Updated: January 28, 2007