![picture](/_resources/images/qtcat.png) - [К списку документов](Readme.md) # Implicit shared в Qt Многие классы C++ в Qt используют неявное совместное использование данных для максимального использования ресурсов и минимизации копирования. Неявно совместно используемые классы являются как безопасными, так и эффективными, когда передаются в качестве аргументов, поскольку передается только указатель на данные, а данные копируются только тогда, когда функция записывает в них данные, т. е.copy-on-write. Implicit sharing в Qt — это механизм, при котором при копировании классов не происходит копирование данных, а копирование происходит лишь тогда, когда копии класса потребуется изменить эти данные.
Некоторые особенности механизма: - При создании общего объекта счётчик ссылок устанавливается в 1. - Когда новый объект ссылается на общие данные, счётчик ссылок увеличивается. - Когда объект теряет ссылку на общие данные, счётчик ссылок уменьшается. - Общие данные удаляются, когда счётчик ссылок становится равен 0. Implicit sharing реализован во многих классах C++ в Qt.
При разработке таких классов используется паттерн Pimpl.
## Pimpl Pimpl — Pointer to private implementation. Это одно из названий паттерна программирования. Еще его называют чеширским котом — «Cheshire Cat» (это название мне больше нравится). В чем суть этого паттерна? Основная идея этого паттерна — это вынести все приватные члены класса и, в некоторых случаях, функционала в приватный класс.
В Qt коде используется подход `d`-указателей. Смысл в том что объявляется класс `XXXPrivate` и переменная публичного класса в защищенной секции. В отдельном заголовочном файле или в `.cpp` файле уже пишется реализация приватного класса. Иерархия классов идет как по публичным так и по приватным классам. Для этого объявление приватного класса обычно делается в отдельном `.h` файле, который называется так-же как публичный, но добавляется приставка `_p`: `qclassname_p.h`. И эти классы не устанавливаются вместе с библиотекой, а служат лишь для сборки библиотеки.
## Пример класса с использованием 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 #include #include #include 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 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 // Закрытый класс 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()) ) qRegisterMetaType(); } 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` можно использовать для передачи в сигналах и для конвертации в/из `QVariant`.
---
- [К списку документов](Readme.md)