578 lines
22 KiB
Markdown
578 lines
22 KiB
Markdown

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