22 KiB
Qt Code Style - Правила кодирования с использованием Qt C++
Ниже представлен обзор соглашений по написанию кода с использованием Qt. При использовании Qt предпочтительно использовать стиль кодирования, который используется в библиотеке, чтобы добиться единообразия кода.
Содержание
- Кодировка
- Заголовочные файлы
- Директивы препроцессора
- Отступы
- Объявление переменных, функций и классов
- Пробелы
- Фигурные скобки
- Круглые скобки
- Использование конструкции switch
- Разрыв строк
- Использование исключений
- Наследование и ключевое слово virtual
- Неявные преобразования
- Числа с плавающей запятой
- Преинкремент и предекремент
- Другие очевидные вещи
- Общее исключение
Кодировка
Единственной кодировкой должна быть UTF-8, а использование других следует приравнять к разжиганию межнациональной розни и карать соответствующей статьёй УК.
Заголовочные файлы
При включении заголовочных файлов Qt, всегда используйте следующую форму записи:
#include <QtCore/QObject>
Префикс библиотеки (QtCore
, QtWidgets
и т.д.) необходим для фреймворков Mac OS X,
а также очень целесообразен для не qmake проектов.
Все заголовочные файлы должны быть с защитой от повторного включения посредством #pragma once
или #define
.
Применение #pragma once
вместо #define
увеличит скорость компиляции во многих случаях
благодаря высокоуровневому механизму; компилятор может самостоятельно сравнивать имена файлов
или inode'ы без необходимости вызова препроцессора для проверки заголовка на наличие #ifndef
и #endif
.
Можно использовать обе команды, #pragma once
и #define
, для написания переносимого кода,
что также может принести выгоду от применения #pragma once
при оптимизации (если компилятор её поддерживает):
#pragma once
#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_
//...
#endif // FOO_BAR_BAZ_H_
-
Все заголовочные файлы должны быть самодостаточными в плане компиляции. Пользователи и инструменты разработки не должны зависеть от специальных зависимостей при использовании заголовочного файла.
-
Подключайте только действительно используемые заголовочные файлы. Не надо заранее подключать кучу
.h
файлов "на всякий случай". -
Никогда не пишите реализацию методов класса или функций в заголовочном файле!
-
Вставляйте заголовочные файлы в следующем порядке: парный файл (например, foo.h — foo.cpp), файлы библиотеки Qt, стандартная библиотека C++, другие библиотеки, файлы вашего проекта.
-
Все заголовочные файлы проекта должны указываться относительно директории исходных файлов проекта без использования таких псевдонимов как . (текущая директория) или .. (родительская директория). Директории поиска общих заголовочных файлов можно задать в файле проекта.
Директивы препроцессора
Символ решетки всегда в начале строки, а имя директивы с отступом.
# if defined(QT_NO_KEYWORDS)
# define QT_NO_EMIT
# else
# ifndef QT_NO_SIGNALS_SLOTS_KEYWORDS
# define slots Q_SLOTS
# define signals Q_SIGNALS
# endif
# endif
Отступы
- Для обозначения отступа используйте 4 пробела подряд.
- Используйте пробелы, а не табуляцию!
Объявление переменных, функций и классов
Один из самых важных постулатов, так как определяет читабельность и общий стиль кода. Правила объявления переменных собраны в такой список:
- Объявляйте по одной переменной в строке.
- Избегайте, если это возможно, коротких и запутанных названий переменных (Например: "a", "rbarr", "nughdeget").
- Односимвольные имена переменных подходят только для итераторов циклов, небольшого локального контекста и временных переменных. В остальных случаях имя переменной должно отражать ее назначение.
- Заводите переменные только по мере необходимости.
// Неправильно:
int a, b;
char *c, *d;
// Правильно:
int height;
int width;
char *nameOfThis;
char *nameOfThat;
-
Не используйте глобальные переменные. Вместо них рекомендуется использовать локальные переменные и передавать их через параметры функций.
-
Функции и переменные должны именоваться со строчной буквы. Каждое последующие слово в имени переменной должно начинаться с прописной буквы.
-
Избегайте аббревиатур.
// Неправильно
short Cntr;
char ITEM_DELIM = ' ';
// Правильно
short counter;
char itemDelimiter = ' ';
- Имена классов всегда начинаются с заглавной буквы.
- Сокращения в camel case (правильно:
QXmlStreamReader
, не правильно:QXMLStreamReader
).
Размещайте свой код в пространстве имён (за некоторыми исключениями).
Пространство имён должно иметь уникальное имя, обычно формируемое на основе названия проекта,
и, возможно, пути.
Не используйте директиву using
(например, using namespace foo
).
Не используйте встроенные (inline
) пространства имён.
Пространства имён делят глобальную область видимости на отдельные именованные области,
позволяя избежать совпадения (коллизий) имён.
Пробелы
Пробелы — очень важный элемент форматирования исходного кода. Он отыгрывает очень большую роль в читабельности кода.
- Используйте пустые строки для логической группировки операторов, где это возможно.
- Всегда используйте одну пустую строку в качестве разделителя
- Всегда используйте один пробел перед фигурной скобкой
// Неправильно:
if(foo){
}
// Правильно:
if (foo) {
}
- Всегда ставьте один пробел после
'*'
или'&'
, если они стоят перед описанием типов. Но никогда не ставьте пробелы между'*'
или'&'
и именем переменной.
char *x;
const QString &myString;
const char * const y = "hello";
- Не окружайте пробелами функции доступа
.
и->
. - Бинарные операции отделяются пробелами с двух сторон.
- После преобразования типов не ставьте пробелов.
- Избегайте преобразования типов в стиле C.
// Неправильно
char* blockOfMemory = (char* ) malloc(data.size());
// Правильно
char *blockOfMemory = reinterpret_cast<char *>(malloc(data.size()));
- Не используйте несколько операторов на одной строке
- По возможности, используйте новую строку для тела оператора ветвления:
// Неправильно:
if (foo) bar();
// Правильно:
if (foo)
bar();
- Содержимое в пространстве имён пишется без отступа.
- Никогда не добавляйте пробелы в конец строки.
Фигурные скобки
Скобки — это вообще отдельная тема. Они, как и пробелы, отыгрывают львиную долю во внешнем виде и читабельности кода.
- Возьмите за основу расстановку открывающих фигурных скобок на одной строке с выражением, которому они предшествуют.
- Исключение: Тело функции и описание класса всегда открывается фигурной скобкой, стоящей на новой строке.
- Используйте фигурные скобки в условиях, если тело условия в размере превышает одну линию, или тело условия достаточное сложное и выделение скобками действительно необходимо.
- Исключение 1: Используйте скобки, если родительское выражение состоит из нескольких строк / оберток.
- Исключение 2: Используйте фигурные скобки, когда тела ветвлений
if-then-else
занимают несколько строчек. - Используйте фигурные скобки для обозначения пустого тела условия.
// Неправильно:
if (codec)
{
// do something
}
// Правильно:
if (codec) {
// do something
}
// Неправильно:
if (address.isEmpty() || !isValid()
|| !codec)
return false;
// Правильно:
if (address.isEmpty() || !isValid()
|| !codec) {
return false;
}
Круглые скобки
- Используйте круглые скобки для группировки выражений.
// Неправильно
if (a && b || c)
if (a + b & c)
// Правильно
if ((a && b) || c)
if ((a + b) & c)
Использование конструкции switch
Безусловно, эти условия причина многих дискуссий со стороны разработчиков и создателей Coding Guidelines — там может быть очень много различных вариантов. Однако Qt предлагает именно такой вариант:
- Операторы case должны быть в одном столбце со
switch
. - Каждый оператор
case
должен иметь закрывающийbreak
(илиreturn
) или комментарий, которой предполагает намеренное отсутствиеbreak & return
.
switch (myEnum) {
case Value1:
doSomething();
break;
case Value2:
case Value3:
doSomethingElse();
// fall through
default:
defaultHandling();
break;
}
Разрыв строк
Часто происходит следующее: есть разработчик Вася с большим монитором. Он пишет себе код спокойно. Потом этот код открывает разработчик Петя, с ноутбуком, и прозревает — ему приходится много скроллить чтобы прочитать весь код. Это один из дефектов читабельности. Что же предлагает Qt для спасения?
- Длина строки кода не должна превышать 100 символов (а лучше 80 - исторический стандарт). Если надо – используйте разрыв строки.
- Запятые помещаются в конец разорванной линии; операторы помещаются в начало новой строки. В зависимости от используемой вами IDE, оператор на конце разорванной строки можно проглядеть.
// Неправильно:
if (longExpression +
otherLongExpression +
otherOtherLongExpression) {
}
QMessageBox::information(d->someObjectWithLongName, tr("A long title for mesage"), tr("A very very very very very very long body"), QMessageBox::Ok, QMessageBox::Cancel);
// Правильно:
if (longExpression
+ otherLongExpression
+ otherOtherLongExpression) {
}
QMessageBox::information(d->someObjectWithLongName,
tr("A long title for mesage"),
tr("A very very very very very very long body"),
QMessageBox::Ok, QMessageBox::Cancel);
Использование исключений
В Qt исключения (throw
) не используются. Вместо них применяются коды ошибок.
Наследование и ключевое слово virtual
В этом постулате все предельно просто — главное не писать virtual
перед названием переопределяемого метода в .h
файле.
// Неправильно:
class MyWidget : public QWidget
{
...
protected:
virtual void keyPressEvent(QKeyEvent *);
// Правильно:
class MyWidget : public QWidget
{
...
protected:
void keyPressEvent(QKeyEvent *) override;
Неявные преобразования
Не объявляйте неявные преобразования. Используйте ключевое слово explicit
для операторов преобразования типа и конструкторов с одним аргументом.
Исключение: конструкторы копирования и перемещения могут объявляться без explicit
,
т.к. они не выполняют преобразование типов.
Числа с плавающей запятой
Числа с плавающей запятой всегда должны быть с десятичной точкой и числами по обе стороны от неё (даже в случае экспоненциальной нотации).
// Неправильно:
float f = 1.f;
long double ld = -.5L;
double d = 1248e6;
// Правильно:
float floatValue = 1.0f;
float floatVariable = 1.0; // OK
double doubleValue = 1248.0e6;
Преинкремент и предекремент
Используйте префиксные формы (++i
) инкремента и декремента;
постфиксную форму используйте только при явной необходимости.
Когда переменная инкрементируется (++i
, i++
) или декрементируется
(--i
, i--
), а возвращаемое значение не используется, то необходимо
чётко понимать: использовать префиксную форму (++i
, --i
) или постфиксную
(i++
, i--
).
Префиксную форму обычно легче читать, и она часто более эффективна (как минимум такая же), т.к. не требуется создавать копию значения до выполнения операции.
// Неправильно:
for (int i = 0; i < size; i++) {
// Правильно:
for (int i = 0; i < size; ++i) {
Другие очевидные вещи
Еще некоторые правила, которые всегда должны выполняться, их все знают, но повторим на всякий случай:
- Не используйте
goto
и другие устаревшие конструкции, которые нарушают принципы стандарта программирования. - По возможности не используйте в проектах
qmake
, старайтесь новые проекты строить с использованиемcmake
актуальной версии. - Всегда включайте в свои файлы проектов расширренную проверку на ошибки.
Для компилятора
gcc
(mingw
) это делается добавлением строчки вCMakeList.txt
:
target_compile_options(${PROJECT_NAME} PRIVATE -Werror -Wall -Wextra -Wpedantic)
- Также можно выключать устаревшие конструкции языка. Пример для выключения
foreach
:
target_compile_definitions(${PROJECT_NAME} PRIVATE QT_NO_FOREACH)
-
Пользуйтесь системой управления версиями.
-
Пишите по возможности кроссплатформенный код (как минимум поддерживаемый Windows и Linux). Используйте условную компиляцию для кода, специфичного для определенной ОС:
# if defined (Q_OS_WINDOWS)
// код для Windows
# else
// код для Linux, MacOS
# endif
-
Для указателей (адресов) используйте
nullptr
, это улучшает безопасность типов. Используйте'\0'
в качестве символа конца строки (пустого символа). Это улучшает читабельность кода. -
Рекомендуется использовать
sizeof(переменная)
вместоsizeof(тип)
. -
Не вставляйте бесполезные комментарии в код. Ваш код должен быть самодокументируемым. Комментарии можно использовать для отдельных пояснений неочевидных или специфических вещей:
// Плохо:
int counter; // - счетчик шагов
// Хорошо:
int stepCounter {0};
Общее исключение
Этот постулат гласит нам, что нет ничего плохого если вы нарушите какое-то правило, но только в том случае, если оно делает ваш код уродливым. Это хороший пример того, что у всех правил есть исключения, и того, что правила созданы чтобы их нарушать.
К сожалению, я не смог найти примера, когда Qt Coding Guidelines
делают код — уродливым.