本页内容

QSharedDataPointer Class

template <typename T> class QSharedDataPointer

QSharedDataPointer 类表示对一个隐式共享对象的指针。更多内容...

头文件: #include <QSharedDataPointer>
CMake: find_package(Qt6 REQUIRED COMPONENTS Core)
target_link_libraries(mytarget PRIVATE Qt6::Core)
qmake: QT += core
继承自: QSharedDataPointerBase

注意:本类中的所有函数均为可重入的。

QSharedDataPointer 比较

类别可比较的类型
strongQSharedDataPointer
strongT* 和 std::nullptr_t。

公共类型

公共函数

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()
operator 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!=(T *const &lhs, const QSharedDataPointer<T> &rhs)
bool operator!=(const QSharedDataPointer<T> &lhs, const QSharedDataPointer<T> &rhs)
bool operator==(T *const &lhs, const QSharedDataPointer<T> &rhs)
bool operator==(const QSharedDataPointer<T> &lhs, const QSharedDataPointer<T> &rhs)

详细说明

QSharedDataPointer 是一个模板类,其中模板参数T 指定了从QSharedData 派生的共享数据类的类型。

QSharedDataPointer<T> 使编写自己的隐式共享类变得非常简单。QSharedDataPointer 实现了线程安全的引用计数机制,确保将 QSharedDataPointer 添加到可重入类中不会导致这些类变得不可重入。

许多 Qt 类都使用隐式共享,以将指针的速度和内存效率与类的易用性相结合。有关更多信息,请参阅“共享类”页面。

假设您希望将Employee 类设为隐式共享。具体步骤如下:

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

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

#include <QSharedData>
#include <QSharedDataPointer>
#include <QString>

class EmployeeData : public QSharedData
{
public:
    EmployeeData() : id(-1) {}
    EmployeeData(const EmployeeData &other)
        : QSharedData(other), id(other.id), name(other.name) {}
    ~EmployeeData() = default;

    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) = default;
    Employee &operator=(const Employee &other) = default;

    Employee(Employee &&other) = default;
    Employee &operator=(Employee &&other) = default;

    ~Employee() = default;

    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) = default;
Employee &operator=(const Employee &other) = default;

Employee(Employee &&other) = default;
Employee &operator=(Employee &&other) = default;

~Employee() = default;

此处严格来说并不需要复制构造函数,因为类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,因此在更改姓名之前会执行“写时复制”(copy-on-write)。此时,e1 和e2 分别指向不同的EmployeeData 对象。它们虽然名字不同,但 ID 均为 1001,这可能并非你所期望的结果。 当然,如果你确实打算创建第二个唯一的员工,可以直接继续执行e1.setId(1002) ;但如果你只是想在所有地方更改该员工的姓名,建议在Employee 类中使用explicit sharing ,而不是隐式共享。

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

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

针对在 Qt 容器中的使用进行性能优化

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

另请参阅 QSharedData 、QExplicitlySharedDataPointer 、QScopedPointer 以及QSharedPointer 。

成员类型文档

QSharedDataPointer::Type

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

成员函数文档

[noexcept] QSharedDataPointer::QSharedDataPointer()

构建一个 QSharedDataPointer,其d 指针初始化为nullptr 。

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

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

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

通过移动构造器创建一个 QSharedDataPointer 实例,使其指向与o 所指向的相同对象。

QSharedDataPointer::~QSharedDataPointer()

将共享数据对象的引用计数减1。如果引用计数变为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,则该函数会创建该共享数据对象的深度拷贝,并将this的d 指针设置为该拷贝。

如果需要“写时复制”(copy-on-write)机制,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 的引用计数(前提是ptr 不是nullptr )。旧的共享数据对象的引用计数将递减,若引用计数降至 0,则该对象将被删除。

该函数于 Qt 6.0 中引入。

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

将此共享数据指针与other 互换。此操作速度极快,且绝不会失败。

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

返回指向共享对象的指针,并将该指针重置为nullptr 。(也就是说,该函数将this的d指针设置为nullptr 。)

注意: 返回对象的引用计数不会被递减。该函数可与接受QAdoptSharedDataTag 标记对象的构造函数配合使用,以在不涉及原子操作的情况下传输共享数据对象。

该函数在 Qt 6.0 中引入。

QSharedDataPointer::operator T *()

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

另请参阅 data() 和constData()。

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

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

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

如果该对象的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)

将other 通过Move-assigns操作赋值给该QSharedDataPointer 实例。

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

将此对象的d 指针设置为o ,并递增o 的引用计数。此对象的旧共享数据对象的引用计数将减 1。如果旧共享数据对象的引用计数变为 0,则该旧共享数据对象将被删除。

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

将本对象的d 指针设置为o 的d 指针,并递增共享数据对象的引用计数。本对象原有共享数据对象的引用计数将递减。如果原有共享数据对象的引用计数变为 0,则该对象将被删除。

相关的非成员

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

如果rhs 的d指针 不是 lhs ,则返回true 。d指针。该函数不会调用detach()。

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

如果 `lhs ` 和 `rhs ` 的`d` 指针 不相同,则返回 `true `。该函数不会调用 `detach()`。

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

如果rhs 的d指针指向lhs ,则返回true 。该函数不会调用detach()。

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

如果 `lhs ` 和 `rhs ` 指向同一个`d` 指针,则返回 `true `。该函数不会调用 `detach()`。

© 2026 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.