Правила кодирования для языка C++

20.06.2014
Нормальный процесс разработки не может происходить без договоренностей об одинаковом форматировании и организации кода. Много копий сломано в спорах по этой теме - как же правильно ставить пробелы, нужно ли делать перенос после круглой скобочки, а уж варианты именования переменных... Спорить мы на эту тему не будем, просто выложим свои правила.

Правила кодирования для языка C++

1. Файлы и каталоги

1.1 Запрещено создавать каталоги или файлы с не латинскими символами в именах. Имена каталогов не должны содержать прописных (заглавных) букв.

1.2 Проект должен содержать следующую структуру каталогов:

trunk
    build
    docs
    src

1.3 Все заголовочные файлы должны быть защищены от повторного “включения”. Нужно использовать операторы условной компиляции, использование #pragma once запрещено. Формат имени _<NAMESPACE>_<SUBNAMESPACE>_<CLASS or FILE>_H_.

#ifndef _STYX_COMMON_SHAREDPTR_H_
#define _STYX_COMMON_SHAREDPTR_H_
...
#endif //_STYX_COMMON_SHAREDPTR_H_

1.4 При создании класса или интерфейса нужно создавать пару файлов с именами, содержащими имя класса (интерфейса) без префикса C(I).

CWindow - Window.h Window.cpp
CGUIButton - GUIButton.h GUIButton.cpp
ICore – Core.h Core.cpp

1.5 При включении файлов необходимо поддерживать следующий порядок:

  • C system files.
  • C++ system files.
  • Other libraries' .h files.
  • Your libraries' .h files.
  • Your project's .h files.
  • Current class .h file.

1.5.1 Нельзя использовать обратный слеш (\) при включении файлов.

1.5.2 При включении файлов пути должны быть указаны с учетом регистра.

#include <sys/types.h>
#include <unistd.h>
#include <map>
#include <vector>
 
#include "common/SharedPtr.h"
#include "MyClass.h"

1.6 Каждый файл должен заканчиваться символом перевода строки.

2. Форматирование исходных текстов

2.1 При организации отступа строки (indent), используются только пробелы. При этом величина каждого отступа равна четырем пробелам. Примечание: В среде Visual Studio 2005 это устанавливается в Tools→Options→Text Editor→C/C++→Tabs.

2.2 Максимальная длина строки кода 77 символов. При невозможности соблюдения этого требования длина строки может доходить максимум до 100 символов.

2.3 Открывающая и закрывающая скобки составного оператора, функции, пространства имен (namespace) и т.п., располагаются каждая на отдельной строке с отступом, величина которого должна быть равной величине отступа оператора, функции, пространства имен и т.п. Кроме пробелов и, возможно, комментариев, скобки являются единственными символами в строке.

2.4 Необходимо комментировать закрывающую скобку namespace его именем.

namespace common
{
    …
} // common

2.5 Не допускается наличие пробела перед запятой, а после запятой обязательно ставится только один пробел.

2.6 Не допускается наличие пробела после открывающей круглой скобки и перед закрывающей соответственно.

a = (b + d) / (c * d);    // right
e = f - ((g & h) >> 10);  // right
e =f-( (g&h ) >>10);      // wrong

Совет: если вы сомневаетесь, нужно ставить скобки в выражении или нет — поставьте их.

2.7 Пробел после ключевых слов do, while, for и других операторов не ставится. Рекомендуется расставлять пробелы в условиях, для того чтобы разделить выражения по смыслу.

for(size_t i = 0; i < nMax; ++i)
{
    …
}

2.8 Величина отступа ключевых слов case и default оператора switch, должна быть равной одному Tab (4 пробела) отступу от ключевого слова switch. Код внутри case вместе с break имеет отступ еще на Tab. Между break и case вставляется пустая строка. Наличие default обязательно.

switch(eValue)
{
    case EValue_First:
        …
        break;
 
    case EValue_Second:
        {
            …
        }
        break;
 
    default:
        ASSERT(0);
        break;
}

2.9 Рекомендуется разделять логические блоки пустыми строками. Две пустые строки подряд не допускаются.

2.10 При описании указателей и ссылок, символы «*» и «&» располагаются сразу после имен типов, к которым относятся указатели и ссылки. В соответствии с правилами C++, чтение описаний таких указателей и ссылок производится справа налево.

CObject*& pObject; // Ссылка на указатель на CObject.

2.11 При написании блоков if/else всегда нужно использовать фигурные скобки. Допускается написание блока if (без else) без фигурных скобок. Возможно написание блока как в одну так и в две строки, но при выборе написания необходимо быть осторожным, т.к. здесь можно совершить ошибку или усложнить читаемость кода.

if(a)
{
    doSomething();
}
else
{
    ...
}
 
if(b)
    doSomething();
 
if(c) doSomething();
// Плохо в две строки, возможна ошибка
if(something)
    // func1(); // если убрать комментарий
    func2();
// Плохо в одну строку, сложно читать. 
if(pChar) *(pChar) = '\0';

2.12 Каждая функция отделяется от предыдущей одной пустой строкой и строкой из /.

///////////////////////////////////////////////////////////
void someFunction1()
{
 
}
 
///////////////////////////////////////////////////////////
void someFunction2()
{
 
}

2.13 Имена членов и методов класса в его объявлении выравниваются на 21-ю колонку.

///////////////////////////////////////////////////////////
сlass CMyClass
{
    int             m_intValue;
    int             _getValue() const;
};

2.14 Слово «template» перед шаблонными классами, функциями, и определениями методов шаблонных классов надо писать на отдельной строке. Названия шаблонных параметров пишутся прописными буквами, слова внутри названия разделяются подчеркиваниями.

///////////////////////////////////////////////////////////
template<PARAM>
class CMyClassT
{
public:
    void            myMethod();
 
    template<METHOD_PARAM>
    void            myMethodT();
};
 
///////////////////////////////////////////////////////////
template<PARAM>
void CMyClassT<PARAM>::myMethod()
{
    …
}
 
///////////////////////////////////////////////////////////
template<METHOD_PARAM>
template<PARAM>
void CMyClassT<PARAM>::myMethodT()
{
    …
}

3. Использование комментариев

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

//
//
///////////////////////////////////////////////////////////

3.2 Комментарии должны быть на английском языке. Допускается писать “временные” комментарии на русском, в дальнейшем подобные комментарии должны быть удалены или переписаны на английском.

3.3 Комментарии не должны допускать возможности двойственного понимания смысла текста.

3.4 Все неочевидные места в коде необходимо снабжать подробными комментариями. Операции, связанные с обходом ошибок внешних библиотек, обязательно снабжаются комментариями.

Совет: нужно стремиться писать самодокументируемый код.

3.5 При написании комментариев отступы строк кода и комментариев устанавливаются одинаковые.

///////////////////////////////////////////////////////////
void someFunction()
{
    // comment block 1
    bool boolCreateTable = pITable->isCreateable();
 
    if(boolCreateTable)
    {
        pITable->create();
    }
 
    // comment block 2
    bool boolSort;
    ...
}

3.6 Если что-то не доделано или вы считаете, что в этом месте могут быть проблемы, обязательно помечайте словом TODO. Перед сдачей проекта все TODO должны быть удалены.

// TODO: optimize this code

4. Правила присвоения имен

4.1 Имена переменных.

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

'[prefix]type[Name]'

Соответственно 'prefix' и 'Name' могут отсутствовать, а вот 'type' должен быть всегда. Исключением могут быть только переменные внутри небольших циклов - i, j.

Допустимые варианты:

'prefixtypeName'
'typeName'
'type'
'prefixtype' - не является допустимым, m_int выглядит некорректно.

Понятно, что зарезервированные имена типов (int, char) не могут быть использованы в качестве имени переменной. Префикс это:

  • m_ для членов классов (member)
  • s_ для статических членов классов и статических переменных (static)
  • g_ для глобальных статических переменных (global)

Совет: глобальные переменные использовать не рекомендуется.

Сокращения для типов приведены в таблице:

Тип Сокращение
Int int
int64 int64
char char
unsigned char uchar
int intCount
int64 int64TotalSize
std::string strString
std::string str
QDialog dlg
QDialog* m_pDlgInfo

Имена констант должны быть в верхнем регистре.

static const int s_intMAX_SIZE

Если переменная хранит значение в каких-то единицах, желательно указывать эти единицы.

int intWidthInMeters

4.2 Имена функций.

Имя функции должно начинаться с маленькой буквы (Java-стиль, Qt-стиль). Исключением могут быть имена функций во внешних по отношению к проекту интерфейсах. Из имени функции должно быть понятно ее назначение. Первым “словом” в названии должен быть глагол, все остальные “слова” начинаются с большой буквы.

const char* getName();
bool isValidSymbol(int intSymbol);
bool connectToServer();

Возможно использование не глаголов в “общепринятых” случаях — size(), begin(), end().

Имена методов класса подчиняется таким же правилам, за исключением private и protected методов — к имени таких методов в начале дописывается “_”.

class A
{
    bool    _init();
public:
    int     getMaxSize();
};

4.3 Имена типов.

В именах типов не должны использоваться глаголы. Имя класса должно начинаться с буквы “C”. Имя шаблонного типа должно заканчиваться на “T”. Имя типа должно начинаться на “T”. Имя enum начинается с буквы “E”, имена констант внутри enum содержат полное имя “родительского” enum. Имя структуры начинается на “S”.

class CPoint;
 
enum EState
{
    EState_Ok,
    EState_Failed
};

template<>
class CPointT;
 
typedef std::vector<int> TIntVector;
 
struct SPoint;

Совет: структура не должна содержать методов, иначе рекомендуется сделать ее классом.

4.4 Для имен namespace используйте только строчные буквы.

4.5 Используйте только прописные буквы в именах макросов. Слова при этом разделяются подчеркиваниями. Рекомендуется начинать имя макроса с имени проекта или солюшена, чтобы избежать конфликта имен.

#define MYPROJECT_ASSERT ASSERT

5. Использование макросов и операторов условной компиляции

5.1 По возможности избегайте использования макросов, используйте функции, const и enum.

5.2 Комментируйте “закрывающие” части операторов.

#ifdef STYX_WIN32
    // ...
#else // STYX_WIN32
    // ...
#endif // STYX_WIN32

5.3 Использование NULL запрещено.

6. Общие рекомендации по кодированию

6.1 Магические числа применять запрещено, кроме необходимых случаев.

6.2 Производительность.

Technical Report on C++ Performance

6.3 Для уменьшения времени компиляции по возможности используйте forward declaration.

6.4 Запрещено использовать RTTI.

6.5 Необходимо использовать приведение типов в стиле C++.

  • Используйте static_cast как замену приведению в стиле C.
  • Используйте const_cast для “снятия константности”. const_cast разрешено использовать только для передачи константного объекта в функции сторонних библиотек.
  • Используйте reinterpret_cast для не безопасного приведения в стиле C.
  • Использование dynamic_cast запрещено.

6.6 Используйте const везде, где это возможно.

6.7 Используйте чужой опыт.

Common Programming Tasks

Последние записи блога

Напишите нам

Хотите разработать приложение? Требуется логотип или сайт?

Мы открыты для сотрудничества! Вы можете отправить нам сообщение прямо сейчас.

+7

Обновить