QSharedDataPointer 类

template <typename T> class QSharedDataPointer

QSharedDataPointer 类表示对隐式共享对象的指针。 更多信息...

头文件 #include <QSharedDataPointer>
CMakefind_package(Qt6 REQUIRED COMPONENTS Core)
target_link_libraries(mytarget PRIVATE Qt6::Core)
qmakeQT += core

注意: 本类中所有函数都是可重入的。 更多信息...

公共类型

公共函数

QSharedDataPointer()
QSharedDataPointer(T *data)
(从 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
voiddetach()
(从 6.0 开始) T *get()
(从 6.0 开始) const T *get() const
(从 6.0 开始) voidreset(T *ptr = nullptr)
voidswap(QSharedDataPointer<T> &other)
(从 6.0 开始) T *take()
T *operator T *()
const T *operator const T *() const
booloperator!() const
T &operator*()
const T &operator*() const
T *operator->()
const T *operator->() const
QSharedDataPointer<T> &operator=(const QSharedDataPointer<T> &o)
QSharedDataPointer<T> &operator=(T *o)
QSharedDataPointer<T> &operator=(QSharedDataPointer<T> &&other)

保护函数

T *clone()
booloperator!=(const QSharedDataPointer<T> &lhs, const QSharedDataPointer<T> &rhs)
booloperator!=(const T *ptr, const QSharedDataPointer<T> &rhs)
booloperator==(const QSharedDataPointer<T> &lhs, const QSharedDataPointer<T> &rhs)
booloperator==(const T *ptr, const QSharedDataPointer<T> &rhs)

详细说明

QSharedDataPointer使得编写自己的隐式共享类变得简单。QSharedDataPointer实现了线程安全引用计数,确保将QSharedDataPointer添加到您的可重入类中不会使其变成不可重入。

许多Qt类使用隐式共享来结合指针的速度和内存效率以及类使用的方便性。更多信息请参阅共享类页面。

假设您想使一个Employee类隐式共享。操作步骤如下

  • 定义类Employee,使其只有一个类型为QSharedDataPointer的数据成员。
  • 定义从QSharedData派生的EmployeeData类,以包含您通常会在Employee类中放入的所有数据成员。

为了实际展示这一点,我们回顾了隐式共享的Employee类的源代码。在头文件中,我们定义了两个类EmployeeEmployeeData

#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指针。所有对员工数据的访问都必须通过该指针的operator->()方法。对于写访问,operator->()将自动调用detach(),该函数会在共享数据对象的引用计数大于1时创建一个共享数据对象的副本。这确保了对一个Employee对象的写入不会影响任何其他共享同一个EmployeeData对象的Employee对象。

EmployeeData类继承自QSharedData,提供了后台引用计数器。EmployeeData具有默认构造函数、拷贝构造函数和析构函数。通常,在数据类中,对于隐式共享类,所有这些的实现都是必需的。

实现类Employee的两个构造函数也是相当直接的。它们都创建一个新的EmployeeData实例并将其分配给指针

    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 的非常量成员函数中,每次解引用 d 指针 时,QSharedDataPointer 会自动调用 detach() 确保函数操作的是其自己的数据副本。

    void setId(int id) { d->id = id; }

    void setName(const QString &name) { d->name = name; }

注意,如果成员函数中多次解引用 d 指针 导致多次调用 detach(),则在第一次调用时,如果调用了一次,detach() 将只会创建共享数据的一个副本;在第二次和随后的调用中,引用计数将再次回到 1。

但是请注意,在第二个包含员工 ID 和名称的 Employee 构造函数中,同时调用了 setId() 和 setName(),但它们不会引起 写时复制,因为新构建的 EmployeeData 对象的引用计数刚刚被设置为 1。

Employeeconst 成员函数中,解引用 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 分配给它之后,e1e2 都指向阿尔布雷希特·丢勒,员工号 1001。两个 Employee 对象都指向相同的 EmployeeData 实例,具有引用计数 2。然后调用 e1.setName("Hans Holbein") 来更改员工姓名,但由于引用计数大于 1,在更改姓名之前执行了 写时复制。此时,e1e2 指向不同的 EmployeeData 对象。它们具有不同的姓名,但都具有相同的 ID 1001,这可能不是您想要的。当然,您可以继续进行 e1.setId(1002),如果您真的想要创建一个独特的第二个员工,但如果您只想更改员工名称,请考虑在 Employee 类中使用显式共享而不是隐式共享。

如果您将 Employee 类中的 d 指针 声明为 QExplicitlySharedDataPointer<EmployeeData>,那么将使用显式共享,并且将不会自动执行 写时复制 操作(即,非常量函数中不会调用 detach())。在这种情况下,在调用 e1.setName("Hans Holbein") 后,员工的名字已更改,但 e1 和 e2 仍然指向相同的 EmployeeData 实例,因此只有一个具有 ID 1001 的员工。

在成员函数文档中,d 指针 总是指向共享数据对象的内部指针。

优化在 Qt 容器中使用时的性能

如果您共享的类与上面的 Employee 类相似,并且仅将 QSharedDataPointer 或 QExplicitlySharedDataPointer 作为成员,那么您应该考虑使用 Q_DECLARE_TYPEINFO() 宏将其标记为可移动类型。这在使用 Qt 的 容器类 时可以提高性能和内存效率。

另请参阅 QSharedDataQExplicitlySharedDataPointerQScopedPointerQSharedPointer

成员类型文档

QSharedDataPointer::Type

这是共享数据对象的类型。指向此类型对象的指针是 d指针

成员函数文档

[noexcept] QSharedDataPointer::QSharedDataPointer()

使用 nullptr 作为 d指针 来初始化 QSharedDataPointer。

[explicit noexcept] QSharedDataPointer::QSharedDataPointer(T *data)

使用 设置为 data 创建一个 QSharedDataPointer,并增加 data 的引用计数。

[noexcept, since 6.0] QSharedDataPointer::QSharedDataPointer(T *data, QAdoptSharedDataTag)

使用 d指针 设置为 data 创建一个 QSharedDataPointer。不会增加 data 的引用计数;这可以用来采用从 take() 获得的指针。

此功能自 Qt 6.0 引入。

另请参阅 take()。

[noexcept] QSharedDataPointer::QSharedDataPointer(const QSharedDataPointer<T> &o)

d指针 设置为 o 中的 d指针,并增加共享数据对象的引用计数。

[noexcept] QSharedDataPointer::QSharedDataPointer(QSharedDataPointer<T> &&o)

移动构造函数实例,使其指向与 o 相同的对象。

QSharedDataPointer::~QSharedDataPointer()

减少共享数据对象的引用计数。如果引用计数变为 0,则删除共享数据对象。然后销毁它。

[protected] T *QSharedDataPointer::clone()

创建并返回当前数据的深拷贝。在引用计数大于 1 时,此函数由 detach() 调用以创建新副本。此函数使用 operator new 并调用类型 T 的复制构造函数。

提供此函数是为了支持您自己的类型的 "虚复制构造函数"。为了做到这一点,您应该为您的类型声明一个函数模板的特化,如下面的示例所示

    template<>
    EmployeeData *QSharedDataPointer<EmployeeData>::clone()
    {
        return d->clone();
    }

在上面的示例中,clone()函数的模板特化调用了EmployeeData::clone()虚函数。从EmployeeData派生的类可以重写该函数并返回正确的多态类型。

[noexcept] const T *QSharedDataPointer::constData() const

返回指向共享数据对象的const指针。此函数不调用detach()。

另请参阅:data().

T *QSharedDataPointer::data()

返回指向共享数据对象的指针。此函数调用detach()。

另请参阅:constData().

[noexcept] const T *QSharedDataPointer::data() const

返回指向共享数据对象的指针。此函数不调用detach()。

void QSharedDataPointer::detach()

如果共享数据对象的引用计数大于1,此函数将创建共享数据对象的深度副本并将此的d指针设置为副本。

此函数在QSharedDataPointer的非const成员函数需要复制时自动调用。您无需自行调用它。

[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)

将此的d指针设置为ptr,如果ptr不是nullptr,则增加ptr的引用计数。旧共享数据对象的引用计数递减,如果引用计数达到0,则删除对象。

此功能自 Qt 6.0 引入。

[noexcept] void QSharedDataPointer::swap(QSharedDataPointer<T> &other)

交换此实例的共享数据指针与other中的共享数据指针。

[noexcept, since 6.0] T *QSharedDataPointer::take()

返回对共享对象的指针,并将此重置为nullptr。(即,此函数将此的d指针设置为nullptr。)

注意:返回对象的引用计数将不会被递减。此函数可以与接受QAdoptSharedDataTag标签对象的构造函数一起使用,以在没有干预原子操作的情况下传输共享数据对象。

此功能自 Qt 6.0 引入。

T *QSharedDataPointer::operator T *()

返回指向共享数据对象的指针。此函数调用detach()。

另请参阅:data()和constData().

[noexcept] const T *QSharedDataPointer::operator const T *() const

返回指向共享数据对象的指针。此函数不调用detach()。

[noexcept] bool QSharedDataPointer::operator!() const

如果 d 指针nullptr,则返回 true

T &QSharedDataPointer::operator*()

提供对共享数据对象成员的访问。此函数调用 detach

const T &QSharedDataPointer::operator*() const

提供对共享数据对象成员的const访问。此函数不会调用 detach

T *QSharedDataPointer::operator->()

提供对共享数据对象成员的访问。此函数调用 detach

[noexcept] const T *QSharedDataPointer::operator->() const

提供对共享数据对象成员的const访问。此函数不会调用 detach

[noexcept] QSharedDataPointer<T> &QSharedDataPointer::operator=(const QSharedDataPointer<T> &o)

d 指针 设置为 并增加共享数据对象的引用计数。 的旧的共享数据对象的引用计数会递减。如果旧的共享数据对象的引用计数变为0,则删除旧的共享数据对象。

[noexcept] QSharedDataPointer<T> &QSharedDataPointer::operator=(T *o)

d 指针 设置为 并增加 的引用计数。 的旧的共享数据对象的引用计数会递减。如果旧的共享数据对象的引用计数变为0,则删除旧的共享数据对象。

[noexcept] QSharedDataPointer<T> &QSharedDataPointer::operator=(QSharedDataPointer<T> &&other)

other

相关非成员

[noexcept] bool operator!=(const QSharedDataPointer<T> &lhs, const QSharedDataPointer<T> &rhs)

如果 不同,则返回 true。此函数不会调用 detach

[noexcept] bool operator!=(const T *ptr, const QSharedDataPointer<T> &rhs)

如果 不是 ,则返回 true。此函数不会调用 detach

[noexcept] bool operator==(QSharedDataPointer<T> &lhs, const QSharedDataPointer<T> &rhs)

如果 lhsrhsd 指针 相同,则返回 true。此函数不调用 detach ()。

[noexcept] bool operator==(T *ptr, const QSharedDataPointer<T> &rhs)

如果 rhsd 指针ptr,则返回 true。此函数不调用 detach ()。

© 2024 Qt 公司 Ltd. 本文件中的文档贡献归其各自所有者所有。本文件提供的文档是在自由软件基金会的 GNU 自由文档许可证 1.3 版本 下授权的。Qt 及其相关标志是芬兰的 Qt 公司及其在全球的其他国家的 商标。所有其他商标是其各自所有者的财产。