Класс QSharedDataPointer
Класс QSharedDataPointer представляет указатель на неявное совместно используемый объект. Подробнее...
| Заголовок: | #include <QSharedDataPointer> |
| qmake: | QT += core |
| С момента: | Qt 4.0 |
Примечание: Все функции в этом классе являются реентерабельными.
Типы
| typedef | Type |
Открытые функции
| QSharedDataPointer() | |
| QSharedDataPointer(T *sharedData) | |
| QSharedDataPointer(const QSharedDataPointer<T> &other) | |
| QSharedDataPointer(QSharedDataPointer &&o) | |
| ~QSharedDataPointer() | |
| const T * | constData() const |
| T * | data() |
| const T * | data() const |
| void | detach() |
| void | swap(QSharedDataPointer &other) |
| operator T *() | |
| operator const T *() const | |
| bool | operator!() const |
| bool | operator!=(const QSharedDataPointer<T> &other) const |
| T & | operator*() |
| const T & | operator*() const |
| T * | operator->() |
| const T * | operator->() const |
| QSharedDataPointer<T> & | operator=(const QSharedDataPointer<T> &other) |
| QSharedDataPointer & | operator=(T *sharedData) |
| QSharedDataPointer<T> & | operator=(QSharedDataPointer<T> &&other) |
| bool | operator==(const QSharedDataPointer<T> &other) const |
Защищённые функции
| T * | clone() |
Подробное описание
Класс QSharedDataPointer представляет указатель на неявное совместно используемый объект.
QSharedDataPointer<T> упрощает создание собственных классов с неявным совместным использованием. QSharedDataPointer реализует безопасную для потоков систему подсчета ссылок, гарантируя, что добавление QSharedDataPointer в ваши реентерабельные классы не сделает их нереентерабельными.
Неявное совместное использование используется многими классами 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 имеет конструктор по умолчанию, конструктор копирования и деструктор. Обычно для класса с неявным совместным использованием в классе данных достаточно тривиальных реализаций этих методов.
Реализация двух конструкторов для класса 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.
В неконстантном члене функции 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 (с ID и именем сотрудника) вызываются как setId(), так и setName(), но они не вызывают «копирование при записи», так как счетчик ссылок для только что созданного объекта EmployeeData установлен в 1.
В константных члене функций Employee обращение к указателю d не вызывает вызов detach().
int id() const { return d->id; }
QString name() const { return d->name; } Обратите внимание, что нет необходимости реализовывать копирующий конструктор или оператор присваивания для класса Employee, так как копирующий конструктор и оператор присваивания, предоставляемые компилятором C++, выполнят требуемую поэлементную поверхностную копию. Единственный член, который нужно скопировать, — это указатель d, который является QSharedDataPointer, чья operator=() просто увеличивает счетчик ссылок на общий 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, выполняется копирование при записи перед изменением имени. Теперь e1 и e2 указывают на разные объекты EmployeeData. У них разные имена, но оба имеют ID 1001, что, вероятно, не то, что вы хотите. Конечно, вы можете просто продолжить с e1.setId(1002), если действительно хотите создать второго, уникального сотрудника, но если вы хотите изменить имя сотрудника везде, рассмотрите использование явного совместного использования в классе Employee вместо неявного совместного использования.
Если объявить указатель d в классе Employee как QExplicitlySharedDataPointer<EmployeeData>, то используется явное совместное использование, и операции копирования при записи не выполняются автоматически (т. е. detach() не вызывается в неконстантных функциях). В этом случае после e1.setName("Hans Holbein"), имя сотрудника было изменено, но e1 и e2 всё ещё ссылаются на один и тот же экземпляр EmployeeData, поэтому существует только один сотрудник с ID 1001.
В документации по функциям членов указатель d всегда относится к внутреннему указателю на объект общих данных.
Оптимизация производительности для использования в контейнерах Qt
Вы должны рассмотреть возможность маркировки вашего класса с неявным совместным использованием как перемещаемого типа, используя макрос Q_DECLARE_TYPEINFO(), если он похож на класс Employee выше и использует QSharedDataPointer или QExplicitlySharedDataPointer в качестве единственного члена. Это может улучшить производительность и эффективность памяти при использовании контейнеров Qt.
См. также QSharedData, QExplicitlySharedDataPointer, QScopedPointer и QSharedPointer.
Документация по типам членов
typedef QSharedDataPointer::Type
Это тип объекта общих данных. Указатель d указывает на объект этого типа.
Документация по функциям членов
QSharedDataPointer::QSharedDataPointer()
Создаёт QSharedDataPointer, инициализированный нулевым указателем d.
QSharedDataPointer::QSharedDataPointer(T *sharedData)
Создаёт QSharedDataPointer с указателем d, установленным на sharedData, и увеличивает счетчик ссылок sharedData.
QSharedDataPointer::QSharedDataPointer(const QSharedDataPointer<T> &other)
Устанавливает указатель d объекта this на указатель d в other и увеличивает счетчик ссылок на объект общих данных.
QSharedDataPointer::QSharedDataPointer(QSharedDataPointer &&o)
Перемещающий конструктор QSharedDataPointer экземпляра, делая его указывающим на тот же объект, на который указывал o.
Эта функция была добавлена в Qt 5.2.
QSharedDataPointer::~QSharedDataPointer()
Уменьшает счетчик ссылок на объект общих данных. Если счетчик ссылок становится 0, объект общих данных удаляется. Затем уничтожается объект this.
[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 объекта this на копию.
Эта функция вызывается автоматически неконстантными функциями-членами QSharedDataPointer, если требуется копирование при записи. Вам не нужно вызывать её самостоятельно.
void QSharedDataPointer::swap(QSharedDataPointer &other)
Меняет указатель на общие данные текущего экземпляра с указателем на общие данные в other.
QSharedDataPointer::operator T *()
Возвращает указатель на объект общих данных. Эта функция вызывает detach().
См. также data() и constData().
QSharedDataPointer::operator const T *() const
Возвращает указатель на объект общих данных. Эта функция не вызывает detach().
bool QSharedDataPointer::operator!() const
Возвращает true, если указатель d объекта this равен нулю.
bool QSharedDataPointer::operator!=(const QSharedDataPointer<T> &other) const
Возвращает true, если other и this не имеют одинаковый указатель 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=(const QSharedDataPointer<T> &other)
Устанавливает указатель d объекта this на указатель d объекта other и увеличивает счетчик ссылок на объект общих данных. Счётчик ссылок на старый объект общих данных this уменьшается. Если счётчик ссылок на старый объект общих данных становится 0, старый объект общих данных удаляется.
QSharedDataPointer &QSharedDataPointer::operator=(T *sharedData)
Устанавливает указатель d объекта this на sharedData и увеличивает счётчик ссылок sharedData. Счётчик ссылок на старый объект общих данных this уменьшается. Если счётчик ссылок на старый объект общих данных становится 0, старый объект общих данных удаляется.
QSharedDataPointer<T> &QSharedDataPointer::operator=(QSharedDataPointer<T> &&other)
Перемещает присваивание other этому экземпляру QSharedDataPointer.
Эта функция была добавлена в Qt 5.2.
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.9/qshareddatapointer.html