QSharedDataPointer Class
template <typename T> class QSharedDataPointerQSharedDataPointer 类表示指向隐式共享对象的指针。更多
头文件: | #include <QSharedDataPointer> |
CMake: | find_package(Qt6 REQUIRED COMPONENTS Core) target_link_libraries(mytarget PRIVATE Qt6::Core) |
qmake: | QT += core |
注意:该类中的所有函数都是可重入的。
公共类型
公共函数
QSharedDataPointer() | |
QSharedDataPointer(T *data) | |
(since 6.0) | QSharedDataPointer(T *data, QAdoptSharedDataTag) |
QSharedDataPointer(const QSharedDataPointer<T> &o) | |
QSharedDataPointer(QSharedDataPointer<T> &&o) | |
~QSharedDataPointer() | |
const T * | constData() const |
T * | data() |
const T * | data() const |
void | detach() |
(since 6.0) T * | get() |
(since 6.0) const T * | get() const |
(since 6.0) void | reset(T *ptr = nullptr) |
void | swap(QSharedDataPointer<T> &other) |
(since 6.0) T * | take() |
T * | operator T *() |
const T * | operator const T *() const |
bool | operator!() const |
T & | operator*() |
const T & | operator*() const |
T * | operator->() |
const T * | operator->() const |
QSharedDataPointer<T> & | operator=(QSharedDataPointer<T> &&other) |
QSharedDataPointer<T> & | operator=(T *o) |
QSharedDataPointer<T> & | operator=(const QSharedDataPointer<T> &o) |
受保护函数
T * | clone() |
相关非成员
bool | operator!=(const QSharedDataPointer<T> &lhs, const QSharedDataPointer<T> &rhs) |
bool | operator!=(const T *ptr, const QSharedDataPointer<T> &rhs) |
bool | operator==(const QSharedDataPointer<T> &lhs, const QSharedDataPointer<T> &rhs) |
bool | operator==(const T *ptr, const QSharedDataPointer<T> &rhs) |
详细说明
QSharedDataPointer<T> 可让您轻松编写自己的隐式共享类。QSharedDataPointer 实现了线程安全的引用计数,确保在可重入类中添加 QSharedDataPointers 不会使这些类变得不可重入。
许多 Qt 类都使用隐式共享,将指针的速度和内存效率与类的易用性结合起来。更多信息,请参阅共享类页面。
假设您想让Employee
类隐式共享。步骤如下
- 定义类
Employee
,使其具有一个类型为QSharedDataPointer<EmployeeData>
的数据成员。 - 定义从QSharedData 派生的
EmployeeData
类,使其包含通常放在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
中,请注意唯一的数据成员,即类型为QSharedDataPointer<EmployeeData>
的d 指针。对雇员数据的所有访问都必须通过d 指针的 operator->()
。对于写入访问,operator->()
会自动调用detach() ,如果共享数据对象的引用计数大于 1,则会创建共享数据对象的副本。这样可以确保对一个Employee
对象的写入不会影响共享相同EmployeeData
对象的其他Employee
对象。
类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
中。相反,我们只需在employee.h
中预先声明私有子类EmployeeData
即可:
class EmployeeData;
如果我们在这里这样做,就需要使用所示的复制构造函数。由于复制构造函数很琐碎,还不如始终包含它。
在幕后,每当复制、分配或作为参数传递Employee
对象时,QSharedDataPointer 都会自动增加引用计数。每当Employee
对象被删除或退出作用域时,它都会减少引用计数。如果引用计数为 0,共享的EmployeeData
对象将自动删除。
在Employee
的非 Const 成员函数中,每当d 指针被取消引用时,QSharedDataPointer 都会自动调用detach() 以确保函数在其自身的数据副本上运行。
void setId(int id) { d->id = id; } void setName(const QString &name) { d->name = name; }
请注意,如果在成员函数中由于多次取消引用d 指针而不止一次调用detach() ,那么detach() 只会在第一次调用时创建共享数据的副本(如果有的话),因为在第二次和后续调用detach() 时,引用计数将再次为 1。
但要注意的是,在第二个Employee
构造函数中(该构造函数接收雇员 ID 和姓名),setId() 和 setName() 都会被调用,但它们不会在写入时产生拷贝,因为新构造的EmployeeData
对象的引用计数刚刚被设置为 1。
在Employee
的const成员函数中,取消引用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 Albrecht Durer。两个Employee
对象都指向EmployeeData
的同一个实例,该实例的引用计数为 2。然后调用e1.setName("Hans Holbein")
来更改雇员姓名,但由于引用计数大于 1,因此在更改姓名之前要执行写入复制。现在,e1
和e2
指向不同的EmployeeData
对象。它们的名称不同,但 ID 都是 1001,这可能不是你想要的。当然,如果你真的想创建第二个唯一的雇员,可以继续使用e1.setId(1002)
,但如果你只想在任何地方更改雇员的姓名,可以考虑在Employee
类中使用explicit sharing ,而不是隐式共享。
如果您将Employee
类中的d 指针声明为QExplicitlySharedDataPointer<EmployeeData>
,那么就会使用显式共享,并且不会自动执行写操作时的复制(即不会在非定式函数中调用detach() )。在这种情况下,在e1.setName("Hans Holbein")
之后,雇员的姓名已经更改,但 e1 和 e2 仍然引用同一个EmployeeData
实例,因此只有一个 ID 为 1001 的雇员。
在成员函数文档中,d 指针总是指共享数据对象的内部指针。
优化 Qt 容器中的使用性能
如果您的隐式共享类类似于上面的Employee
类,并且使用 QSharedDataPointer 或QExplicitlySharedDataPointer 作为唯一成员,那么您应该考虑使用Q_DECLARE_TYPEINFO() 宏将其标记为可移动类型。这可以提高使用 Qt容器类时的性能和内存效率。
另请参见 QSharedData,QExplicitlySharedDataPointer,QScopedPointer, 和QSharedPointer 。
成员函数文档
[noexcept]
QSharedDataPointer::QSharedDataPointer()
构造一个 QSharedDataPointer,以nullptr
作为d 指针初始化。
[explicit noexcept]
QSharedDataPointer::QSharedDataPointer(T *data)
构造一个 QSharedDataPointer,其d 指针设置为data ,并增加data 的引用计数。
[noexcept, since 6.0]
QSharedDataPointer::QSharedDataPointer(T *data, QAdoptSharedDataTag)
构造一个 QSharedDataPointer,其d 指针设置为data 。data 的引用计数器不会递增;这可用于采用从take() 获得的指针。
此函数在 Qt 6.0 中引入。
另请参阅 take()。
[noexcept]
QSharedDataPointer::QSharedDataPointer(const QSharedDataPointer<T> &o)
将this的d 指针设置为o 中的d 指针,并增加共享数据对象的引用计数。
[noexcept]
QSharedDataPointer::QSharedDataPointer(QSharedDataPointer<T> &&o)
Move- 构造一个 QSharedDataPointer 实例,使其指向o 所指向的同一对象。
QSharedDataPointer::~QSharedDataPointer()
减少共享数据对象的引用计数。如果引用计数为 0,共享数据对象将被删除。然后将其销毁。
[protected]
T *QSharedDataPointer::clone()
创建并返回当前数据的深度副本。当引用计数大于 1 时,detach() 会调用该函数来创建新副本。该函数使用操作符 new并调用 T 类型的拷贝构造函数。
提供该函数的目的是为您自己的类型支持 "虚拟拷贝构造函数"。为此,您应为自己的类型声明该函数的模板特化,如下例所示:
template<> EmployeeData *QSharedDataPointer<EmployeeData>::clone() { return d->clone(); }
在上例中,clone() 函数的模板特化调用了EmployeeData::clone()虚拟函数。从 EmployeeData 派生的类可以覆盖该函数,并返回适当的多态类型。
[noexcept]
const T *QSharedDataPointer::constData() const
返回指向共享数据对象的常量指针。此函数不会调用detach()。
另请参阅 data()。
T *QSharedDataPointer::data()
返回指向共享数据对象的指针。此函数调用detach()。
另请参见 constData()。
[noexcept]
const T *QSharedDataPointer::data() const
返回指向共享数据对象的指针。此函数不调用detach().
void QSharedDataPointer::detach()
如果共享数据对象的引用计数大于 1,该函数将创建共享数据对象的深度副本,并将其 d 指针设置为副本。
如果需要在写入时复制, QSharedDataPointer 的非常数成员函数会自动调用该函数。您无需自行调用。
[since 6.0]
T *QSharedDataPointer::get()
与data() 相同。提供此函数是为了与 STL 兼容。
此函数在 Qt 6.0 中引入。
[noexcept, since 6.0]
const T *QSharedDataPointer::get() const
与data() 相同。提供此函数是为了与 STL 兼容。
此函数在 Qt 6.0 中引入。
[noexcept, since 6.0]
void QSharedDataPointer::reset(T *ptr = nullptr)
将this的d 指针设置为ptr ,如果ptr 不是nullptr
,则增加ptr 的引用计数。如果引用计数为 0,则递减旧共享数据对象的引用计数并删除该对象。
此函数在 Qt 6.0 中引入。
[noexcept]
void QSharedDataPointer::swap(QSharedDataPointer<T> &other)
将此共享数据指针与other 互换。这一操作速度非常快,而且从未出现过故障。
[noexcept, since 6.0]
T *QSharedDataPointer::take()
返回指向共享对象的指针,并将this重置为nullptr
。(也就是说,该函数将this的d 指针设置为nullptr
。)
注意: 返回对象的引用计数不会递减。该函数可与接收QAdoptSharedDataTag 标记对象的构造函数一起使用,在不进行原子操作的情况下传输共享数据对象。
此函数在 Qt 6.0 中引入。
T *QSharedDataPointer::operator T *()
返回指向共享数据对象的指针。该函数调用detach()。
[noexcept]
const T *QSharedDataPointer::operator const T *() const
返回指向共享数据对象的指针。此函数不调用detach().
[noexcept]
bool QSharedDataPointer::operator!() const
如果this的d 指针为nullptr
,则返回true
。
T &QSharedDataPointer::operator*()
提供对共享数据对象成员的访问。该函数调用detach().
const T &QSharedDataPointer::operator*() const
提供对共享数据对象成员的常量访问。此函数不调用detach().
T *QSharedDataPointer::operator->()
提供对共享数据对象成员的访问。该函数调用detach().
[noexcept]
const T *QSharedDataPointer::operator->() const
提供对共享数据对象成员的常量访问。此函数不调用detach().
[noexcept]
QSharedDataPointer<T> &QSharedDataPointer::operator=(QSharedDataPointer<T> &&other)
Move-assignsother 到此QSharedDataPointer 实例。
[noexcept]
QSharedDataPointer<T> &QSharedDataPointer::operator=(T *o)
将this的d 指针设置为o ,并递增o 的引用计数。this的旧共享数据对象的引用计数会递减。如果旧共享数据对象的引用计数变为 0,则删除旧共享数据对象。
[noexcept]
QSharedDataPointer<T> &QSharedDataPointer::operator=(const QSharedDataPointer<T> &o)
将this的d 指针设置为o 的d 指针,并增加共享数据对象的引用计数。this的旧共享数据对象的引用计数会递减。如果旧共享数据对象的引用计数变为 0,则删除旧共享数据对象。
相关非成员
[noexcept]
bool operator!=(const QSharedDataPointer<T> &lhs, const QSharedDataPointer<T> &rhs)
如果lhs 和rhs 的d 指针 不一致,则返回true
。此函数不调用detach().
[noexcept]
bool operator!=(const T *ptr, const QSharedDataPointer<T> &rhs)
如果rhs 的d 指针 不是 ptr 的d 指针,则返回true
。此函数不调用detach().
[noexcept]
bool operator==(const QSharedDataPointer<T> &lhs, const QSharedDataPointer<T> &rhs)
如果lhs 和rhs 的d 指针相同,则返回true
。此函数不调用detach().
[noexcept]
bool operator==(const T *ptr, const QSharedDataPointer<T> &rhs)
如果rhs 的d 指针是ptr ,则返回true
。此函数不调用detach() 。
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.