Класс QSharedDataPointer
шаблон <typename T> класс QSharedDataPointerКласс QSharedDataPointer представляет указатель на неявно совместно используемый объект. Подробнее...
| Заголовок: | #include <QSharedDataPointer> |
| qmake: | QT += core |
| С момента: | Qt 4.0 |
Этот класс был представлен в Qt 4.0.
Примечание: Все функции в этом классе являются реентерабельными.
Типы public
| typedef | Type |
Функции public
| QSharedDataPointer(QSharedDataPointer<T> &&o) | |
| QSharedDataPointer(const QSharedDataPointer<T> &o) | |
| QSharedDataPointer(T *data) | |
| QSharedDataPointer() | |
| QSharedDataPointer<T> & | operator=(QSharedDataPointer<T> &&other) |
| QSharedDataPointer<T> & | operator=(const QSharedDataPointer<T> &o) |
| ~QSharedDataPointer() | |
| const T * | constData() const |
| T * | data() |
Защищенные функции
| T * | clone() |
Подробное описание
QSharedDataPointer<T> упрощает создание собственных классов с неявным совместным использованием. QSharedDataPointer реализует безопасную для потоков подсчет ссылок, гарантируя, что добавление QSharedDataPointers в ваши реентерабельные классы не сделает их нереентерабельными.
Неявное совместное использование используется многими классами Qt для сочетания скорости и эффективности памяти указателей с удобством использования классов. См. страницу Классы с совместным использованием для получения дополнительной информации.
Предположим, вы хотите сделать класс Employee неявно совместно используемым. Процедура:
- Определите класс
Employeeс единственным членом данных типаQSharedDataPointer<EmployeeData>. - Определите класс
EmployeeData, производный от QSharedData, содержащий все члены данных, которые обычно помещались бы в классEmployee.
Для демонстрации на практике рассмотрим исходный код класса Employee с неявно совместным использованием. В файле заголовка мы определяем два класса Employee и EmployeeData.
#include <QSharedData>
#include <QString>
class EmployeeData : public QSharedData
{
public:
EmployeeData() : id(-1) { }
EmployeeData(const EmployeeData &other)
: QSharedData(other), id(other.id), name(other.name) { }
~EmployeeData() { }
int id;
QString name;
};
class Employee
{
public:
Employee() { d = new EmployeeData; }
Employee(int id, const QString &name) {
d = new EmployeeData;
setId(id);
setName(name);
}
Employee(const Employee &other)
: d (other.d)
{
}
void setId(int id) { d->id = id; }
void setName(const QString &name) { d->name = name; }
int id() const { return d->id; }
QString name() const { return d->name; }
private:
QSharedDataPointer<EmployeeData> d;
}; В классе Employee, обратите внимание на единственный член данных, указатель d типа QSharedDataPointer<EmployeeData>. Все обращение к данным сотрудника должно проходить через указатель d с использованием его operator->(). Для операций записи operator->() автоматически вызовет detach(), что создаст копию общего объекта данных, если счетчик ссылок на общий объект данных больше 1. Это гарантирует, что записи в одном объекте Employee не повлияют на другие объекты Employee, которые используют тот же общий объект EmployeeData.
Класс EmployeeData наследует QSharedData, который обеспечивает счетчик ссылок. EmployeeData имеет конструктор по умолчанию, конструктор копирования и деструктор. Обычно, тривиальные реализации этих методов достаточно в классе data для неявно совместно используемого класса.
Реализация двух конструкторов для класса Employee также проста. Оба создают новый экземпляр EmployeeData и присваивают его указателю d.
Employee() { d = new EmployeeData; }
Employee(int id, const QString &name) {
d = new EmployeeData;
setId(id);
setName(name);
} Обратите внимание, что у класса Employee также определен тривиальный конструктор копирования, который в этом случае не является строго необходимым.
Employee(const Employee &other)
: d (other.d)
{
} Конструктор копирования не строго необходим здесь, потому что класс EmployeeData включён в тот же файл, что и класс Employee (employee.h). Однако, включение приватного подкласса QSharedData в тот же файл, что и публичный класс, содержащий QSharedDataPointer, не является типичным. Обычно цель — скрыть приватный подкласс QSharedData от пользователя, поместив его в отдельный файл, который не будет включаться в публичный файл. В этом случае мы бы поместили класс EmployeeData в отдельный файл, который не включался бы в employee.h. Вместо этого мы бы просто предварительно объявили приватный подкласс EmployeeData в employee.h следующим образом:
class EmployeeData;
Если бы мы сделали это здесь, конструктор копирования был бы необходим. Поскольку конструктор копирования тривиален, вы можете всегда его включать.
За кулисами QSharedDataPointer автоматически увеличивает счетчик ссылок всякий раз, когда объект Employee копируется, присваивается или передаётся в качестве параметра. Он уменьшает счетчик ссылок всякий раз, когда объект Employee удаляется или выходит из области видимости. Общий объект EmployeeData удаляется автоматически, когда счетчик ссылок достигает 0.
В не-const членной функции Employee, когда указатель d разыменовывается, QSharedDataPointer автоматически вызывает detach() для обеспечения того, что функция работает со своей собственной копией данных.
void setId(int id) { d->id = id; }
void setName(const QString &name) { d->name = name; } Обратите внимание, что если detach() вызывается более одного раза в членной функции из-за многократного разыменования указателя d, detach() будет создавать копию общих данных только в первый раз, если вообще, потому что при последующих вызовах detach() счетчик ссылок вновь будет 1.
Но обратите внимание, что во втором конструкторе Employee (который принимает идентификатор сотрудника и имя), вызываются как setId(), так и setName(), но они не вызывают копирование при записи, так как счетчик ссылок на недавно созданный объект EmployeeData только что был установлен в 1.
В const членных функциях Employee, разыменование указателя d не вызывает вызов detach().
int id() const { return d->id; }
QString name() const { return d->name; } Обратите внимание, что нет необходимости реализовывать конструктор копирования или оператор присваивания для класса Employee, потому что конструктор копирования и оператор присваивания, предоставляемые компилятором C++, выполнят необходимую копию членов по одному. Единственный член для копирования — указатель d, который является QSharedDataPointer, чья реализация просто увеличивает счетчик ссылок на общий объект EmployeeData.
Неявное против явного совместного использования
Неявное совместное использование, возможно, не подходит для класса Employee. Рассмотрите простой пример, который создает два экземпляра неявно совместно используемого класса Employee.
#include "employee.h"
int main()
{
Employee e1(1001, "Albrecht Durer");
Employee e2 = e1;
e1.setName("Hans Holbein");
} После создания второго сотрудника e2 и присвоения ему e1, оба e1 и e2 ссылаются на Альбрехта Дюрера, сотрудника 1001. Оба объекта Employee указывают на один и тот же экземпляр EmployeeData, у которого счетчик ссылок равен 2. Затем вызывается e1.setName("Hans Holbein"), чтобы изменить имя сотрудника, но поскольку счетчик ссылок больше 1, перед изменением имени выполняется copy on write. Теперь e1 и e2 указывают на разные объекты EmployeeData. У них разные имена, но оба имеют идентификатор 1001, что, вероятно, не то, что вам нужно. Конечно, вы можете просто продолжить с e1.setId(1002), если действительно хотите создать второго, уникального сотрудника, но если вы хотите только изменить имя сотрудника повсюду, рассмотрите использование явного совместного использования в классе Employee вместо неявного совместного использования.
Если вы объявите указатель d в классе Employee как QExplicitlySharedDataPointer<EmployeeData>, то будет использоваться явное совместное использование, и операции copy on write не будут выполняться автоматически (т.е. detach() не вызывается в неконстантных функциях). В этом случае после e1.setName("Hans Holbein"), имя сотрудника было изменено, но e1 и e2 по-прежнему ссылаются на один и тот же экземпляр EmployeeData, поэтому существует только один сотрудник с идентификатором 1001.
В документации к членам функции d указатель всегда ссылается на внутренний указатель на объект совместно используемых данных.
Оптимизация производительности для использования в контейнерах Qt
Если ваш неявно совместно используемый класс похож на класс Employee выше и использует QSharedDataPointer или QExplicitlySharedDataPointer в качестве единственного члена, следует пометить его как перемещаемый тип, используя макрос Q_DECLARE_TYPEINFO(). Это может улучшить производительность и эффективность использования памяти при работе с контейнерными классами Qt.
См. также QSharedData, QExplicitlySharedDataPointer, QScopedPointer и QSharedPointer.
Документация по типу члена
typedef QSharedDataPointer::Type
Это тип объекта совместно используемых данных. Указатель d указывает на объект этого типа.
Документация по членам функции
QSharedDataPointer::QSharedDataPointer(QSharedDataPointer<T> &&o)
Создает экземпляр QSharedDataPointer путем перемещения, делая его указывающим на тот же объект, на который указывал o.
Эта функция была добавлена в Qt 5.2.
QSharedDataPointer::QSharedDataPointer(const QSharedDataPointer<T> &o)
Устанавливает указатель d для этого на указатель d в o и увеличивает счетчик ссылок на объект совместно используемых данных.
QSharedDataPointer::QSharedDataPointer(T *data)
Создает QSharedDataPointer с указателем d, установленным на data, и увеличивает счетчик ссылок для data.
QSharedDataPointer::QSharedDataPointer()
Создает QSharedDataPointer, инициализированный значением nullptr в качестве указателя d.
QSharedDataPointer<T> &QSharedDataPointer::operator=(QSharedDataPointer<T> &&other)
Перемещает присваивание other данному экземпляру QSharedDataPointer.
Эта функция была добавлена в Qt 5.2.
QSharedDataPointer<T> &QSharedDataPointer::operator=(const QSharedDataPointer<T> &o)
Устанавливает указатель d для этого на указатель d объекта o и увеличивает счетчик ссылок на объект совместно используемых данных. Счетчик ссылок на старый объект совместно используемых данных этого уменьшается. Если счетчик ссылок на старый объект совместно используемых данных становится равным 0, старый объект совместно используемых данных удаляется.
QSharedDataPointer::~QSharedDataPointer()
Уменьшает счетчик ссылок на объект совместно используемых данных. Если счетчик ссылок становится равным 0, объект совместно используемых данных удаляется. После этого это уничтожается.
[protected] T *QSharedDataPointer::clone()
Создает и возвращает глубокую копию текущих данных. Эта функция вызывается функцией detach() при превышении счетчика ссылок значения 1 для создания новой копии. Эта функция использует operator new и вызывает конструктор копирования типа T.
Эта функция предоставляется для поддержки «виртуальных конструкторов копирования» для ваших собственных типов. Для этого следует объявить специализацию шаблона для этой функции для вашего типа, как в примере ниже:
template<>
EmployeeData *QSharedDataPointer<EmployeeData>::clone()
{
return d->clone();
} В примере выше специализация шаблона для функции clone() вызывает виртуальную функцию EmployeeData::clone(). Класс, производный от EmployeeData, может переопределить эту функцию и вернуть соответствующий полиморфный тип.
Эта функция была добавлена в Qt 4.5.
const T *QSharedDataPointer::constData() const
Возвращает постоянный указатель на объект совместно используемых данных. Эта функция не вызывает detach().
См. также data().
T *QSharedDataPointer::data()
Возвращает указатель на объект совместно используемых данных. Эта функция вызывает detach().
См. также constData().
const T *QSharedDataPointer::data() const
Возвращает указатель на объект совместно используемых данных. Эта функция не вызывает detach().
void QSharedDataPointer::detach()
Если счетчик ссылок на объект совместно используемых данных больше 1, эта функция создает глубокую копию объекта совместно используемых данных и устанавливает указатель d для этого на копию.
Эта функция вызывается автоматически неконстантными членами функции QSharedDataPointer, если требуется copy on write. Вам не нужно вызывать её самостоятельно.
void QSharedDataPointer::swap(QSharedDataPointer<T> &other)
Меняет указатель на совместно используемые данные этого экземпляра с указателем на совместно используемые данные в other.
T *QSharedDataPointer::operator T *()
Возвращает указатель на объект совместно используемых данных. Эта функция вызывает detach().
См. также data() и constData().
const T *QSharedDataPointer::operator const T *() const
Возвращает указатель на объект совместно используемых данных. Эта функция не вызывает detach().
bool QSharedDataPointer::operator!() const
Возвращает true если указатель d для этого равен nullptr.
bool QSharedDataPointer::operator!=(const QSharedDataPointer<T> &other) const
Возвращает true если other и это не имеют одинакового указателя d. Эта функция не вызывает detach().
T &QSharedDataPointer::operator*()
Предоставляет доступ к членам объекта совместно используемых данных. Эта функция вызывает detach().
const T &QSharedDataPointer::operator*() const
Предоставляет постоянный доступ к членам объекта совместно используемых данных. Эта функция не вызывает detach().
T *QSharedDataPointer::operator->()
Предоставляет доступ к членам объекта совместно используемых данных. Эта функция вызывает detach().
const T *QSharedDataPointer::operator->() const
Предоставляет постоянный доступ к членам объекта совместно используемых данных. Эта функция не вызывает detach().
QSharedDataPointer<T> &QSharedDataPointer::operator=(T *o)
Устанавливает указатель d для этого на o и увеличивает счетчик ссылок для o. Счетчик ссылок на старый объект совместно используемых данных этого уменьшается. Если счетчик ссылок на старый объект совместно используемых данных становится равным 0, старый объект совместно используемых данных удаляется.
bool QSharedDataPointer::operator==(const QSharedDataPointer<T> &other) const
Возвращает true если other и this имеют одинаковый указатель d. Эта функция не вызывает detach().
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-5.15/qshareddatapointer.html