Initial commit
parent
5db9c9b817
commit
17710ae21d
|
@ -0,0 +1,107 @@
|
||||||
|
.build/
|
||||||
|
_other/
|
||||||
|
_distrib*/
|
||||||
|
|
||||||
|
# Tmp files
|
||||||
|
*~
|
||||||
|
Thumbs.db*
|
||||||
|
|
||||||
|
# C++ objects and libs
|
||||||
|
*.slo
|
||||||
|
*.lo
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.la
|
||||||
|
*.lai
|
||||||
|
*.so
|
||||||
|
*.so.*
|
||||||
|
*.dll
|
||||||
|
*.dylib
|
||||||
|
*.ko
|
||||||
|
*.obj
|
||||||
|
*.elf
|
||||||
|
*.lib
|
||||||
|
|
||||||
|
# Qt-es
|
||||||
|
object_script.*.Release
|
||||||
|
object_script.*.Debug
|
||||||
|
*_plugin_import.cpp
|
||||||
|
/.qmake.cache
|
||||||
|
/.qmake.stash
|
||||||
|
*.pro.user
|
||||||
|
*.pro.user.*
|
||||||
|
*.qbs.user
|
||||||
|
*.qbs.user.*
|
||||||
|
*.moc
|
||||||
|
moc_*.cpp
|
||||||
|
moc_*.h
|
||||||
|
qrc_*.cpp
|
||||||
|
ui_*.h
|
||||||
|
*.qmlc
|
||||||
|
*.jsc
|
||||||
|
Makefile*
|
||||||
|
*build-*
|
||||||
|
*.qm
|
||||||
|
*.prl
|
||||||
|
|
||||||
|
# Qt unit tests
|
||||||
|
target_wrapper.*
|
||||||
|
|
||||||
|
# QtCreator
|
||||||
|
*.autosave
|
||||||
|
|
||||||
|
# QtCreator Qml
|
||||||
|
*.qmlproject.user
|
||||||
|
*.qmlproject.user.*
|
||||||
|
|
||||||
|
# QtCreator CMake
|
||||||
|
CMakeLists.txt.user*
|
||||||
|
|
||||||
|
# QtCreator 4.8< compilation database
|
||||||
|
compile_commands.json
|
||||||
|
|
||||||
|
# QtCreator local machine specific files for imported projects
|
||||||
|
*creator.user*
|
||||||
|
|
||||||
|
*_qmlcache.qrc
|
||||||
|
|
||||||
|
# ---> C
|
||||||
|
# Prerequisites
|
||||||
|
*.d
|
||||||
|
|
||||||
|
# Linker output
|
||||||
|
*.ilk
|
||||||
|
*.map
|
||||||
|
*.exp
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
*.i*86
|
||||||
|
*.x86_64
|
||||||
|
*.hex
|
||||||
|
|
||||||
|
# Debug files
|
||||||
|
*.dSYM/
|
||||||
|
*.su
|
||||||
|
*.idb
|
||||||
|
*.pdb
|
||||||
|
|
||||||
|
# Kernel Module Compile Results
|
||||||
|
*.mod*
|
||||||
|
*.cmd
|
||||||
|
.tmp_versions/
|
||||||
|
modules.order
|
||||||
|
Module.symvers
|
||||||
|
Mkfile.old
|
||||||
|
dkms.conf
|
||||||
|
|
||||||
|
# Fortran module files
|
||||||
|
*.mod
|
||||||
|
*.smod
|
||||||
|
|
|
@ -0,0 +1,254 @@
|
||||||
|

|
||||||
|
|
||||||
|
- [К списку документов](Readme.md)
|
||||||
|
|
||||||
|
# Implicit shared в Qt
|
||||||
|
|
||||||
|
Многие классы C++ в Qt используют неявное совместное использование данных для
|
||||||
|
максимального использования ресурсов и минимизации копирования.
|
||||||
|
Неявно совместно используемые классы являются как безопасными, так и эффективными,
|
||||||
|
когда передаются в качестве аргументов, поскольку передается только указатель на данные,
|
||||||
|
а данные копируются только тогда, когда функция записывает в них данные, т. е.copy-on-write.
|
||||||
|
|
||||||
|
Implicit sharing в Qt — это механизм, при котором при копировании классов
|
||||||
|
не происходит копирование данных, а копирование происходит лишь тогда,
|
||||||
|
когда копии класса потребуется изменить эти данные.
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
Некоторые особенности механизма:
|
||||||
|
|
||||||
|
- При создании общего объекта счётчик ссылок устанавливается в 1.
|
||||||
|
- Когда новый объект ссылается на общие данные, счётчик ссылок увеличивается.
|
||||||
|
- Когда объект теряет ссылку на общие данные, счётчик ссылок уменьшается.
|
||||||
|
- Общие данные удаляются, когда счётчик ссылок становится равен 0.
|
||||||
|
|
||||||
|
Implicit sharing реализован во многих классах C++ в Qt.
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
При разработке таких классов используется паттерн Pimpl.
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
## Pimpl
|
||||||
|
|
||||||
|
Pimpl — Pointer to private implementation.
|
||||||
|
Это одно из названий паттерна программирования.
|
||||||
|
Еще его называют чеширским котом — «Cheshire Cat» (это название
|
||||||
|
мне больше нравится). В чем суть этого паттерна?
|
||||||
|
Основная идея этого паттерна — это вынести все приватные члены класса и,
|
||||||
|
в некоторых случаях, функционала в приватный класс.
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
В Qt коде используется подход `d`-указателей.
|
||||||
|
Смысл в том что объявляется класс `XXXPrivate` и переменная публичного класса
|
||||||
|
в защищенной секции. В отдельном заголовочном файле или в `.cpp` файле уже
|
||||||
|
пишется реализация приватного класса.
|
||||||
|
Иерархия классов идет как по публичным так и по приватным классам.
|
||||||
|
Для этого объявление приватного класса обычно делается в отдельном `.h` файле,
|
||||||
|
который называется так-же как публичный, но добавляется
|
||||||
|
приставка `_p`: `qclassname_p.h`. И эти классы не устанавливаются вместе с
|
||||||
|
библиотекой, а служат лишь для сборки библиотеки.
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
## Пример класса с использованием Implicit shared
|
||||||
|
|
||||||
|
Пример класса для хранения ошибки, в котором используется Implicit shared.
|
||||||
|
|
||||||
|
- [Заголовочный файл `error_object.h`](examples/error_object.h)
|
||||||
|
|
||||||
|
```C
|
||||||
|
#pragma once
|
||||||
|
#ifndef NAYK_ERROR_OBJECT_H
|
||||||
|
#define NAYK_ERROR_OBJECT_H
|
||||||
|
|
||||||
|
#include <QtCore/QMetaType>
|
||||||
|
#include <QtCore/QString>
|
||||||
|
#include <QtCore/QDateTime>
|
||||||
|
#include <QtCore/QSharedDataPointer>
|
||||||
|
|
||||||
|
class ErrorObjectPrivate; // предварительное объявление закрытого класса
|
||||||
|
|
||||||
|
class ErrorObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ErrorObject();
|
||||||
|
ErrorObject(int module, int code, const QString &text,
|
||||||
|
const QDateTime &dateTime = QDateTime::currentDateTime());
|
||||||
|
ErrorObject(const ErrorObject &other);
|
||||||
|
ErrorObject &operator=(const ErrorObject &other);
|
||||||
|
~ErrorObject();
|
||||||
|
int module() const;
|
||||||
|
void setModule(int module);
|
||||||
|
int code() const;
|
||||||
|
void setCode(int code);
|
||||||
|
QString text() const;
|
||||||
|
void setText(const QString &text);
|
||||||
|
QDateTime dateTime() const;
|
||||||
|
void setDateTime(const QDateTime &dateTime);
|
||||||
|
bool isValid() const;
|
||||||
|
bool operator==(const ErrorObject &other) const;
|
||||||
|
bool operator!=(const ErrorObject &other) const;
|
||||||
|
ErrorObject copy() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSharedDataPointer<ErrorObjectPrivate> d;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(ErrorObject)
|
||||||
|
|
||||||
|
#endif // NAYK_ERROR_OBJECT_H
|
||||||
|
```
|
||||||
|
|
||||||
|
- [Файл с реализацией `error_object.cpp`](examples/error_object.cpp) содержит код нашего класса `ErrorObject`
|
||||||
|
и реализацию закрытого класса `ErrorObjectPrivate`
|
||||||
|
|
||||||
|
```C
|
||||||
|
#include "error_object.h"
|
||||||
|
#include <QtCore/QSharedData>
|
||||||
|
|
||||||
|
// Закрытый класс ErrorObjectPrivate:
|
||||||
|
|
||||||
|
class ErrorObjectPrivate: public QSharedData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int module {-1};
|
||||||
|
int code {-1};
|
||||||
|
QString text;
|
||||||
|
QDateTime dateTime {QDateTime::currentDateTime()};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Класс ErrorObject:
|
||||||
|
|
||||||
|
ErrorObject::ErrorObject()
|
||||||
|
: d(new ErrorObjectPrivate)
|
||||||
|
{
|
||||||
|
if ( !QMetaType::isRegistered(qMetaTypeId<ErrorObject>()) )
|
||||||
|
qRegisterMetaType<ErrorObject>();
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorObject::ErrorObject(int module, int code, const QString &text, const QDateTime &dateTime)
|
||||||
|
: d(new ErrorObjectPrivate)
|
||||||
|
{
|
||||||
|
d->module = module;
|
||||||
|
d->code = code;
|
||||||
|
d->text = text;
|
||||||
|
d->dateTime = dateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorObject::ErrorObject(const ErrorObject &other) = default;
|
||||||
|
|
||||||
|
ErrorObject &ErrorObject::operator=(const ErrorObject &other) = default;
|
||||||
|
|
||||||
|
ErrorObject::~ErrorObject() = default;
|
||||||
|
|
||||||
|
int ErrorObject::module() const
|
||||||
|
{
|
||||||
|
return d->module;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ErrorObject::setModule(int module)
|
||||||
|
{
|
||||||
|
d->module = module;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ErrorObject::code() const
|
||||||
|
{
|
||||||
|
return d->code;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ErrorObject::setCode(int code)
|
||||||
|
{
|
||||||
|
d->code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ErrorObject::text() const
|
||||||
|
{
|
||||||
|
return d->text;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ErrorObject::setText(const QString &text)
|
||||||
|
{
|
||||||
|
d->text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDateTime ErrorObject::dateTime() const
|
||||||
|
{
|
||||||
|
return d->dateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ErrorObject::setDateTime(const QDateTime &dateTime)
|
||||||
|
{
|
||||||
|
d->dateTime = dateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ErrorObject::isValid() const
|
||||||
|
{
|
||||||
|
return (d->module >= 0) && ((d->code >= 0) || !d->text.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ErrorObject::operator==(const ErrorObject &other) const
|
||||||
|
{
|
||||||
|
return (d->module == other.d->module) &&
|
||||||
|
(d->dateTime == other.d->dateTime) &&
|
||||||
|
(
|
||||||
|
((d->code == other.d->code) && (d->code >= 0))
|
||||||
|
||
|
||||||
|
((d->code < 0) && (other.d->code < 0) && (d->text == other.text()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ErrorObject::operator!=(const ErrorObject &other) const
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorObject ErrorObject::copy() const
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Пример использования в коде:
|
||||||
|
|
||||||
|
```C
|
||||||
|
#include "error_object.h"
|
||||||
|
//...
|
||||||
|
ErrorObject error(1, 404, "Resource not found");
|
||||||
|
//... какие-то действия
|
||||||
|
ErrorObject otherError = error; // здесь данные не копируются, увеличивается счетчик ссылок
|
||||||
|
qDebug() << "Error code:" << otherError.code();
|
||||||
|
otherError.setCode(200); // здесь происходит копирование данных, затем изменение в копии
|
||||||
|
```
|
||||||
|
|
||||||
|
Для возможности использования типа в метасистеме Qt
|
||||||
|
добавлено объявление типа в `.h` файле:
|
||||||
|
|
||||||
|
```C
|
||||||
|
Q_DECLARE_METATYPE(ErrorObject)
|
||||||
|
```
|
||||||
|
|
||||||
|
и регистрация типа (должна вызываться один раз до использования в метасистеме):
|
||||||
|
|
||||||
|
```C
|
||||||
|
qRegisterMetaType<ErrorObject>();
|
||||||
|
```
|
||||||
|
|
||||||
|
После регистрации тип `ErrorObject` можно использовать для передачи в сигналах и
|
||||||
|
для конвертации в/из `QVariant`.
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
- [К списку документов](Readme.md)
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
|
@ -0,0 +1,577 @@
|
||||||
|

|
||||||
|
|
||||||
|
- [К списку документов](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/>
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|

|
||||||
|
|
||||||
|
- [К списку документов](Readme.md)
|
||||||
|
|
||||||
|
# Qt Creator
|
||||||
|
|
||||||
|
## Горячие клавиши для Qt Creator
|
||||||
|
|
||||||
|
Горячие клавиши для Qt Creator, которые значительно упростят жизнь
|
||||||
|
|
||||||
|
|
||||||
|
- `Esc` - Выполняет переход к редактированию кода.
|
||||||
|
Несколько последовательных нажатий этой клавиши переключают пользователя в режим
|
||||||
|
редактирования, закрывают панели вывода справки, отладки.
|
||||||
|
|
||||||
|
- `F4` - Переключает редактор между файлом реализации (`.сpp`) и соответствующим
|
||||||
|
заголовочным файлом (`.h`), которые содержат объявления интерфейса и
|
||||||
|
реализации класса соответственно.
|
||||||
|
|
||||||
|
- `F2` - Выполняет переход к месту объявления переменной, функции, класса,
|
||||||
|
на имени которых стоял курсор при нажатии.
|
||||||
|
|
||||||
|
- `F1` - Показывает справку для класса или метода Qt, на имени которого стоит курсор.
|
||||||
|
|
||||||
|
- `Ctrl + Shift + R` - Переименование переменной, метода, класса, на имени
|
||||||
|
которых стоит курсор. Имя будет изменено во всех местах, где встречается
|
||||||
|
его использование: не только в текущем файле, но и в других файлах проекта.
|
||||||
|
При замене имени будет учитываться область видимости имени, поэтому замена
|
||||||
|
произойдёт только в местах обращения к имени. Именно этим это действие отличается
|
||||||
|
от обычного поиска и замены текста.
|
||||||
|
|
||||||
|
- `Ctrl + Shift + U` - Поиск всех мест обращения к переменной, методу,
|
||||||
|
классу на имени которого стоит курсор.
|
||||||
|
|
||||||
|
- `Ctrl + K` - Открывает поле быстрого поиска (Locator).
|
||||||
|
|
||||||
|
- `Alt + Enter` - Позволяет открыть доступные дополнительные действия для
|
||||||
|
переменной, метода, класса, оператора в позиции курсора.
|
||||||
|
Это дополнительные действия для рефакторинга (реорганизации и улучшения
|
||||||
|
существующего кода) могут содержать изменение порядка параметров,
|
||||||
|
изменения в текущем фрагменте кода, добавление фрагментов кода и т.д.
|
||||||
|
|
||||||
|
- `Ctrl + Space` - Вызывает выпадающий список автозавершения кода.
|
||||||
|
|
||||||
|
- `Ctrl + F` - Поиск текста в текущем открытом файле.
|
||||||
|
|
||||||
|
- `Ctrl + Shift + F` - Расширенный поиск текста в файле, проекте или группе
|
||||||
|
проектов (доступны дополнительные настройки).
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
- [К списку документов](Readme.md)
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
|
@ -0,0 +1,253 @@
|
||||||
|

|
||||||
|
|
||||||
|
- [К списку документов](Readme.md)
|
||||||
|
|
||||||
|
# QThread - что нужно знать о потоках
|
||||||
|
|
||||||
|
## Основы
|
||||||
|
|
||||||
|
В Qt любые объекты способные работать с сигналами и слотами являются
|
||||||
|
наследниками класса `QObject`.
|
||||||
|
Каждый `QObject` строго привязан к какому-то потоку `QThread` который,
|
||||||
|
собственно, и занимается обслуживанием слотов и прочих событий данного объекта.
|
||||||
|
Один поток может обслуживать сразу множество `QObject` или вообще ни одного,
|
||||||
|
а вот `QObject` всегда имеет родительский поток и он всегда ровно один.
|
||||||
|
По сути можно считать что каждый `QThread` «владеет» каким-то набором `QObject`.
|
||||||
|
Внутри каждого `QThread` спрятана очередь сообщений адресованных
|
||||||
|
к объектам которыми данный `QThread` «владеет».
|
||||||
|
В модели Qt предполагается что если мы хотим чтобы `QObject` сделал
|
||||||
|
какое-либо действие, то мы «посылаем» данному `QObject` сообщение `QEvent`.
|
||||||
|
В этом потоково-безопасном вызове Qt находит `QThread` которому принадлежит
|
||||||
|
объект receiver, записывает `QEvent` в очередь сообщений этого потока и при
|
||||||
|
необходимости «будит» этот поток. При этом ожидается что код работающий в
|
||||||
|
данном `QThread` в какой-то момент после этого прочитает сообщение из
|
||||||
|
очереди и выполнит соответствующее действие. Чтобы это действительно произошло,
|
||||||
|
код в `QThread` должен войти в цикл обработки событий `QEventLoop`,
|
||||||
|
создав соответствующий объект и позвав у него либо метод `exec()`,
|
||||||
|
либо метод `processEvents()`. Первый вариант входит в бесконечный цикл
|
||||||
|
обработки сообщений (до получения `QEventLoop` события `quit()` ),
|
||||||
|
второй ограничивается тем что обрабатывает сообщения ранее накопившиеся в очереди.
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
Итак, как мы уже разобрались, каждый объект в Qt «принадлежит» какому-то потоку.
|
||||||
|
При этом встает закономерный вопрос: а какому, собственно говоря, именно?
|
||||||
|
В Qt приняты следующие соглашения:
|
||||||
|
|
||||||
|
- Все «дети» любого «родителя» всегда живут в том же потоке что и
|
||||||
|
родительский объект. Например попытка сделать `setParent` к объекту живущему
|
||||||
|
в другом потоке в Qt просто молча фейлится (в консоль пишется предупреждение).
|
||||||
|
|
||||||
|
- Объект у которого при создании не указан родитель живет в потоке
|
||||||
|
который его создал.
|
||||||
|
|
||||||
|
- При необходимости поток можно менять вызовом `QObject::moveToThread` (переместить объект в поток).
|
||||||
|
Перемещать можно только верхнеуровневых «родителей» (у которых `parent == null`),
|
||||||
|
попытка переместить любого «ребенка» будет молча проигнорирована.
|
||||||
|
|
||||||
|
- При перемещении верхнеуровневого «родителя» все его «дети» тоже переедут
|
||||||
|
в новый поток.
|
||||||
|
|
||||||
|
- Получить «текущий» поток исполнения можно через вызов
|
||||||
|
функции `QThread::currentThread()`, поток с которым ассоциирован объект — через
|
||||||
|
вызов `QObject::thread()`.
|
||||||
|
|
||||||
|
- Все GUI-объекты кроме rendering back-end должны жить в GUI-потоке.
|
||||||
|
|
||||||
|
- GUI-потоком является тот в котором был создан объект `QApplication`.
|
||||||
|
|
||||||
|
## Таймеры в потоках
|
||||||
|
|
||||||
|
Таймеры `QTimer` в потоках имеют ряд особенностей, которые нужно учитывать
|
||||||
|
при проектировании многопоточных приложений на Qt:
|
||||||
|
|
||||||
|
- `QTimer` работает только в потоке с event loop (циклом обработки событий).
|
||||||
|
Это значит, что если вы хотите использовать `QTimer` в `QThread`, то в этом потоке должен быть запущен `QEventLoop`.
|
||||||
|
Если нет цикла событий — таймер просто не сработает.
|
||||||
|
|
||||||
|
- QTimer должен быть создан в том потоке, в котором будет работать.
|
||||||
|
Таймер "привязан" к потоку через QObject::thread().
|
||||||
|
Если вы создадите таймер в основном потоке, а потом попытаетесь использовать
|
||||||
|
его в другом — это будет ошибка.
|
||||||
|
|
||||||
|
- Если объект с таймером перемещается в другой поток через `moveToThread()`,
|
||||||
|
таймер должен быть создан после перемещения. Иначе он останется привязанным к исходному потоку.
|
||||||
|
|
||||||
|
## Правильная работа с потоками в Qt
|
||||||
|
|
||||||
|
Для правильной работы вашего кода в параллельном потоке используйте следующие правила:
|
||||||
|
|
||||||
|
- Проектируется отдельный класс (worker), потомок QObject, который будет работать в потоке.
|
||||||
|
- У этого объекта должны быть определены публичные слоты для запуска и остановки
|
||||||
|
основной работы, например: `void start();` и `void stop();`.
|
||||||
|
- Также у объекта должны быть определены сигналы для оповещения после начала
|
||||||
|
и после полного окончания работы,
|
||||||
|
например `void started();` и `void finished();`.
|
||||||
|
- Создание внутренних объектов и таймеров должны выполняться только
|
||||||
|
в слоте `start()` или позже (не в конструкторе).
|
||||||
|
- Обмен данными с объектом должен осуществляться только через механизм сигнал-слот.
|
||||||
|
Вызов методов напрямую после помещения объекта в поток запрещен!
|
||||||
|
- Далее в основном потоке программы создаётся экземпляр объекта worker (без родителя),
|
||||||
|
создаётся оъект `QThread`, и worker перемещвется в поток через `moveToThread()`.
|
||||||
|
После этого создаются связи сигнал-слот и запускается поток на выполнение.
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
Пример класса `Worker`. Заголовочный файл `worker.h`:
|
||||||
|
|
||||||
|
```C
|
||||||
|
#pragma once
|
||||||
|
#ifndef WORKER_H_
|
||||||
|
#define WORKER_H_
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtCore/QTimer>
|
||||||
|
|
||||||
|
class Worker: public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit Worker(QObject *parent = nullptr);
|
||||||
|
virtual ~Worker();
|
||||||
|
signals:
|
||||||
|
void started();
|
||||||
|
void finished();
|
||||||
|
// ... другие сигналы наружу
|
||||||
|
public slots:
|
||||||
|
void start();
|
||||||
|
void stop();
|
||||||
|
// ...другие слоты для наружи
|
||||||
|
private:
|
||||||
|
QTimer *m_timer {nullptr};
|
||||||
|
// ...
|
||||||
|
private slots:
|
||||||
|
void timerTimeOut();
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
#endif // WORKER_H_
|
||||||
|
```
|
||||||
|
|
||||||
|
Файл с реализацией `worker.cpp`:
|
||||||
|
|
||||||
|
```C
|
||||||
|
#include "worker.h"
|
||||||
|
|
||||||
|
Worker::Worker(QObject *parent) : QObject{parent}
|
||||||
|
{
|
||||||
|
// конструктор выполняется еще в основном потоке!
|
||||||
|
}
|
||||||
|
|
||||||
|
Worker::~Worker()
|
||||||
|
{
|
||||||
|
// К моменту вызова деструктора поток уже должен быть остановлен!
|
||||||
|
// но проверить стоит:
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Worker::start()
|
||||||
|
{
|
||||||
|
// К этому моменту мы уже в параллельном потоке
|
||||||
|
|
||||||
|
if (m_timer) { // если таймер уже есть, значит это повторный вызов
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_timer = new QTimer(this);
|
||||||
|
connect(m_timer, &QTimer::timeout, this, &Worker::timerTimeOut);
|
||||||
|
// ... другие действия, например настройки и запуск таймера
|
||||||
|
m_timer->start(1000);
|
||||||
|
|
||||||
|
emit started(); // оповещение, что мы стартанули
|
||||||
|
}
|
||||||
|
|
||||||
|
void Worker::stop()
|
||||||
|
{
|
||||||
|
// Тут мы еще в параллельном потоке
|
||||||
|
|
||||||
|
if (!m_timer) { // если таймера нет, значит это повторный вызов
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_timer->stop();
|
||||||
|
delete m_timer;
|
||||||
|
m_timer = nullptr;
|
||||||
|
|
||||||
|
// ... тут остановка всей работы, удаление объектов и т.п.
|
||||||
|
|
||||||
|
emit finished(); // оповещение, что мы остановились
|
||||||
|
}
|
||||||
|
|
||||||
|
void Worker::timerTimeOut()
|
||||||
|
{
|
||||||
|
// Обработчик таймера срабатывает уже в параллельном потоке
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Как применить из основного потока, например слот клика по кнопке:
|
||||||
|
|
||||||
|
```C
|
||||||
|
#include "worker.h"
|
||||||
|
#include <QtCore/QThread>
|
||||||
|
// ...
|
||||||
|
|
||||||
|
void MainWindow::pushButtonClicked()
|
||||||
|
{
|
||||||
|
Worker *worker = new Worker(); // без родителя
|
||||||
|
QThread *thread = new QThread(this);
|
||||||
|
|
||||||
|
worker->moveToThread(thread);
|
||||||
|
|
||||||
|
// Объект начнёт работу после запуска потока:
|
||||||
|
connect(thread, &QThread::started, worker, &Worker::start);
|
||||||
|
|
||||||
|
// Какой-нибудь сигнал для остановки потока:
|
||||||
|
connect(this, &MainWindow::stopThread, worker, &Worker::stop);
|
||||||
|
|
||||||
|
// Когда объект закончит работу, можно останавливать поток:
|
||||||
|
connect(worker, &Worker::finished, thread, &QThread::quit);
|
||||||
|
|
||||||
|
// Когда поток завершится, можно удалить объект и класс потока:
|
||||||
|
connect(thread, &QThread::finished, worker, &Worker::deleteLater);
|
||||||
|
connect(thread, &QThread::finished, thread, &Worker::deleteLater);
|
||||||
|
|
||||||
|
// ... тут другие нужные сигналы-слоты
|
||||||
|
|
||||||
|
// Запуск потока:
|
||||||
|
thread->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// когда поток больше не нужен, вызываем emit stopThread();
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Обратите внимание, в примере выше все действия по созданию и запуску потока находятся
|
||||||
|
в одном слоте и после выхода из слота доступны только через сигналы.
|
||||||
|
Нет защиты от повторного запуска - т.е. сколько раз нажали кнопку, столько потоков и создаётся,
|
||||||
|
а остановка всех по сигналу `stopThread()`.
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
Для отслеживания количества нужно добавить счетчик запуска или флаг, например.
|
||||||
|
И при закрытии вашего приложения нужно обязательно дождаться завершения всех
|
||||||
|
созданных потоков!
|
||||||
|
Но это уже на вашей совести.
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
*p.s. Код не проверял и писал по памяти. Возможны очепятки.*
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
- [К списку документов](Readme.md)
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
16
README.md
16
README.md
|
@ -1,3 +1,15 @@
|
||||||
# Qt_Code_Style
|

|
||||||
|
|
||||||
|
# Сборник полезностей по Qt и C++
|
||||||
|
|
||||||
|
- [Qt Code Style](QtCodeStyle.md) - Правила кодирования с использованием Qt
|
||||||
|
- [Qt Creator](QtCreator.md) - полезности
|
||||||
|
- [Implicit Shared в Qt](ImplicitShared.md) - механизм управления копированием данных
|
||||||
|
- [QThread - что нужно знать о потоках](QtThreads.md) - пример работы с потоками
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
Сборник полезностей Qt
|
|
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
Binary file not shown.
After Width: | Height: | Size: 56 KiB |
|
@ -0,0 +1,138 @@
|
||||||
|
/****************************************************************************
|
||||||
|
** Copyright (c) 2025 Evgeny Teterin (nayk) <nayk@nxt.ru>
|
||||||
|
** All right reserved.
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
** a copy of this software and associated documentation files (the
|
||||||
|
** "Software"), to deal in the Software without restriction, including
|
||||||
|
** without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
** distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
** permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
** the following conditions:
|
||||||
|
**
|
||||||
|
** The above copyright notice and this permission notice shall be
|
||||||
|
** included in all copies or substantial portions of the Software.
|
||||||
|
**
|
||||||
|
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
#include "error_object.h"
|
||||||
|
#include <QtCore/QSharedData>
|
||||||
|
|
||||||
|
namespace nayk { //=============================================================
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
class ErrorObjectData : public QSharedData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int module {-1};
|
||||||
|
int code {-1};
|
||||||
|
QString text;
|
||||||
|
QDateTime dateTime {QDateTime::currentDateTime()};
|
||||||
|
};
|
||||||
|
//==============================================================================
|
||||||
|
void regiserErrorObjectMetaType()
|
||||||
|
{
|
||||||
|
if ( !QMetaType::isRegistered( qMetaTypeId<nayk::ErrorObject>() ) ) {
|
||||||
|
|
||||||
|
qRegisterMetaType<nayk::ErrorObject>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
ErrorObject::ErrorObject()
|
||||||
|
: d(new ErrorObjectData)
|
||||||
|
{
|
||||||
|
regiserErrorObjectMetaType();
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
ErrorObject::ErrorObject(int module, int code, const QString &text, const QDateTime &dateTime)
|
||||||
|
: d(new ErrorObjectData)
|
||||||
|
{
|
||||||
|
regiserErrorObjectMetaType();
|
||||||
|
|
||||||
|
d->module = module;
|
||||||
|
d->code = code;
|
||||||
|
d->text = text;
|
||||||
|
d->dateTime = dateTime;
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
ErrorObject::ErrorObject(const ErrorObject &other) = default;
|
||||||
|
//==============================================================================
|
||||||
|
ErrorObject &ErrorObject::operator=(const ErrorObject &other) = default;
|
||||||
|
//==============================================================================
|
||||||
|
ErrorObject::~ErrorObject() = default;
|
||||||
|
//==============================================================================
|
||||||
|
int ErrorObject::module() const
|
||||||
|
{
|
||||||
|
return d->module;
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
void ErrorObject::setModule(int module)
|
||||||
|
{
|
||||||
|
d->module = module;
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
int ErrorObject::code() const
|
||||||
|
{
|
||||||
|
return d->code;
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
void ErrorObject::setCode(int code)
|
||||||
|
{
|
||||||
|
d->code = code;
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
QString ErrorObject::text() const
|
||||||
|
{
|
||||||
|
return d->text;
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
void ErrorObject::setText(const QString &text)
|
||||||
|
{
|
||||||
|
d->text = text;
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
QDateTime ErrorObject::dateTime() const
|
||||||
|
{
|
||||||
|
return d->dateTime;
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
void ErrorObject::setDateTime(const QDateTime &dateTime)
|
||||||
|
{
|
||||||
|
d->dateTime = dateTime;
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
bool ErrorObject::isValid() const
|
||||||
|
{
|
||||||
|
return (d->module >= 0) && ((d->code >= 0) || !d->text.isEmpty());
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
bool ErrorObject::operator==(const ErrorObject &other) const
|
||||||
|
{
|
||||||
|
return (d->module == other.d->module) &&
|
||||||
|
(d->dateTime == other.d->dateTime) &&
|
||||||
|
(
|
||||||
|
((d->code == other.d->code) && (d->code >= 0))
|
||||||
|
||
|
||||||
|
((d->code < 0) && (other.d->code < 0) && (d->text == other.text()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
bool ErrorObject::operator!=(const ErrorObject &other) const
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
ErrorObject ErrorObject::copy() const
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
} // namespace nayk //==========================================================
|
|
@ -0,0 +1,73 @@
|
||||||
|
/****************************************************************************
|
||||||
|
** Copyright (c) 2025 Evgeny Teterin (nayk) <nayk@nxt.ru>
|
||||||
|
** All right reserved.
|
||||||
|
**
|
||||||
|
** Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
** a copy of this software and associated documentation files (the
|
||||||
|
** "Software"), to deal in the Software without restriction, including
|
||||||
|
** without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
** distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
** permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
** the following conditions:
|
||||||
|
**
|
||||||
|
** The above copyright notice and this permission notice shall be
|
||||||
|
** included in all copies or substantial portions of the Software.
|
||||||
|
**
|
||||||
|
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
#pragma once
|
||||||
|
#ifndef NAYK_ERROR_OBJECT_H
|
||||||
|
#define NAYK_ERROR_OBJECT_H
|
||||||
|
|
||||||
|
#if defined (LIB_NAYK_CORE)
|
||||||
|
# define ERROR_OBJECT_EXPORT Q_DECL_EXPORT
|
||||||
|
#else
|
||||||
|
# define ERROR_OBJECT_EXPORT Q_DECL_IMPORT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <QtCore/QMetaType>
|
||||||
|
#include <QtCore/QString>
|
||||||
|
#include <QtCore/QDateTime>
|
||||||
|
#include <QtCore/QSharedDataPointer>
|
||||||
|
|
||||||
|
namespace nayk { //=============================================================
|
||||||
|
|
||||||
|
class ErrorObjectData;
|
||||||
|
|
||||||
|
class ERROR_OBJECT_EXPORT ErrorObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ErrorObject();
|
||||||
|
ErrorObject(int module, int code, const QString &text, const QDateTime &dateTime = QDateTime::currentDateTime());
|
||||||
|
ErrorObject(const ErrorObject &other);
|
||||||
|
ErrorObject &operator=(const ErrorObject &other);
|
||||||
|
~ErrorObject();
|
||||||
|
int module() const;
|
||||||
|
void setModule(int module);
|
||||||
|
int code() const;
|
||||||
|
void setCode(int code);
|
||||||
|
QString text() const;
|
||||||
|
void setText(const QString &text);
|
||||||
|
QDateTime dateTime() const;
|
||||||
|
void setDateTime(const QDateTime &dateTime);
|
||||||
|
bool isValid() const;
|
||||||
|
bool operator==(const ErrorObject &other) const;
|
||||||
|
bool operator!=(const ErrorObject &other) const;
|
||||||
|
ErrorObject copy() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSharedDataPointer<ErrorObjectData> d;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace nayk //==========================================================
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(nayk::ErrorObject)
|
||||||
|
|
||||||
|
#endif // NAYK_ERROR_OBJECT_H
|
Loading…
Reference in New Issue