Qt隐式共享浅析

一、什么是隐式共享

Qt 的隐式共享(implicit sharing)机制是一种设计模式,用于在进行数据拷贝时提高效率和减少内存占用。

Qt 中,许多类(如 QStringQList 等)都使用了隐式共享机制。这意味着当这些类的实例被拷贝时,实际上并不会立即进行数据的深拷贝,而是共享同一份数据。只有在其中一个实例发生修改时,才会进行实际的数据复制,以确保数据的独立性,即Copy-On-Write

隐式共享机制通过引用计数(reference counting)来实现。每个共享的实例都包含一个引用计数,用于记录当前有多少个实例共享同一份数据。当一个实例被拷贝时,引用计数会增加;当一个实例被销毁时,引用计数会减少。只有当引用计数为 1 时,才会进行实际的数据复制。

这种设计模式可以提高程序的性能和内存利用率,特别是在处理大量数据拷贝的情况下。同时,开发者也无需过多关注数据的共享和拷贝,从而简化了程序的设计和实现。

总之,Qt 的隐式共享机制是一种高效的数据共享和拷贝方式,它通过引用计数来实现数据的延迟复制,从而提高了程序的性能和内存利用率。

二、隐式共享代码示例

下面是一个简单的例子,展示了QString如何使用隐式共享:

cpp 复制代码
#include <QString>
#include <QDebug>

int main() {
    QString str1 = "Hello, Qt!";
    QString str2 = str1; // 这里并没有发生真正的数据复制,str1和str2共享同一份数据,拷贝赋值使用的是浅拷贝的方式

    qDebug() << str1; // 输出 "Hello, Qt!"
    qDebug() << str2; // 输出 "Hello, Qt!"

    str2[0] = 'h'; // 在这里,由于str2要修改数据,所以发生了真正的数据复制,str1和str2不再共享数据

    qDebug() << str1; // 输出 "Hello, Qt!"
    qDebug() << str2; // 输出 "hello, Qt!"
    return 0;
}

在上述代码中,当我们创建str2并将其初始化为str1时,并没有发生真正的数据复制,str1和str2实际上是共享同一份数据的。只有当我们试图修改str2的数据时,才会发生真正的数据复制,这就是所谓的写时复制。

这种技术的优点是可以大大减少不必要的数据复制,从而提高程序的性能。但是,它也有一些缺点,例如在多线程环境中可能需要额外的同步操作,以防止数据竞争。

在Qt的源码中,这种技术的实现主要依赖于引用计数和深拷贝。每个可以共享数据的对象都有一个引用计数,当引用计数为1时,表示只有一个对象在使用这份数据,可以直接修改。当引用计数大于1时,表示有多个对象在共享这份数据,如果有一个对象要修改数据,就需要先进行深拷贝,然后再修改新的数据,这样就不会影响到其他对象。

三、自定义一个使用隐式共享技术的数据类型

Qt中,你可以通过使用QSharedDataQSharedDataPointer类来实现隐式共享(也称为写时复制)。

以下是一个简单的例子,定义了一个自定义的数据类型MyData,它使用了隐式共享技术:

cpp 复制代码
#include <QSharedData>
#include <QSharedDataPointer>

class MyData : public QSharedData {
public:
    MyData() : x(0), y(0) {}
    MyData(int x, int y) : x(x), y(y) {}
    int x, y;
};

class MySharedType {
public:
    MySharedType() : data(new MyData) {}
    MySharedType(int x, int y) : data(new MyData(x, y)) {}
    MySharedType(const MySharedType &other) : data(other.data) {}
    MySharedType &operator=(const MySharedType &other) {
        if (this != &other)
            data = other.data;
        return *this;
    }
    int x() const { return data->x; }
    int y() const { return data->y; }
    void setX(int x) { if (data->x != x) detach(); data->x = x; }
    void setY(int y) { if (data->y != y) detach(); data->y = y; }

private:
    void detach() { if (data->ref != 1) data = new MyData(*data); }
    QSharedDataPointer<MyData> data;
};

在这个例子中,MyData类是实际存储数据的类,它继承自QSharedDataMySharedType类是用户使用的类,它包含一个QSharedDataPointer,指向MyData实例。当需要修改数据时,detach方法会被调用,如果有多个MySharedType实例共享同一个MyData实例,那么detach方法会创建一个新的MyData实例,以实现写时复制。

顺便看看 QSharedDataQSharedDataPointer 的源码实现:

cpp 复制代码
#ifndef QSHAREDDATA_H
#define QSHAREDDATA_H

#include <QtCore/qglobal.h>
#include <QtCore/qatomic.h>
#if QT_DEPRECATED_SINCE(5, 6)
#include <QtCore/qhash.h>
#endif
#include <QtCore/qhashfunctions.h>

QT_BEGIN_NAMESPACE


template <class T> class QSharedDataPointer;

class
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
Q_CORE_EXPORT
#endif
QSharedData
{
public:
    mutable QAtomicInt ref;   /// 原子计算

    inline QSharedData() noexcept : ref(0) { }
    inline QSharedData(const QSharedData &) noexcept : ref(0) { }

    // using the assignment operator would lead to corruption in the ref-counting
    QSharedData &operator=(const QSharedData &) = delete;
    ~QSharedData() = default;
};

template <class T> class QSharedDataPointer
{
public:
    typedef T Type;
    typedef T *pointer;

	/***************************************************/
	/// 分离数据
    inline void detach() { if (d && d->ref.loadRelaxed() != 1) detach_helper(); }
    /***************************************************/


    inline T &operator*() { detach(); return *d; }
    inline const T &operator*() const { return *d; }
    inline T *operator->() { detach(); return d; }
    inline const T *operator->() const { return d; }
    inline operator T *() { detach(); return d; }
    inline operator const T *() const { return d; }
    inline T *data() { detach(); return d; }
    inline const T *data() const { return d; }
    inline const T *constData() const { return d; }

    inline bool operator==(const QSharedDataPointer<T> &other) const { return d == other.d; }
    inline bool operator!=(const QSharedDataPointer<T> &other) const { return d != other.d; }

    inline QSharedDataPointer() { d = nullptr; }
    inline ~QSharedDataPointer() { if (d && !d->ref.deref()) delete d; }

    explicit QSharedDataPointer(T *data) noexcept;
    inline QSharedDataPointer(const QSharedDataPointer<T> &o) : d(o.d) { if (d) d->ref.ref(); }
    inline QSharedDataPointer<T> & operator=(const QSharedDataPointer<T> &o) {
        if (o.d != d) {
            if (o.d)
                o.d->ref.ref();
            T *old = d;
            d = o.d;
            if (old && !old->ref.deref())
                delete old;
        }
        return *this;
    }
    inline QSharedDataPointer &operator=(T *o) {
        if (o != d) {
            if (o)
                o->ref.ref();
            T *old = d;
            d = o;
            if (old && !old->ref.deref())
                delete old;
        }
        return *this;
    }
    QSharedDataPointer(QSharedDataPointer &&o) noexcept : d(o.d) { o.d = nullptr; }
    inline QSharedDataPointer<T> &operator=(QSharedDataPointer<T> &&other) noexcept
    {
        QSharedDataPointer moved(std::move(other));
        swap(moved);
        return *this;
    }

    inline bool operator!() const { return !d; }

    inline void swap(QSharedDataPointer &other) noexcept
    { qSwap(d, other.d); }

protected:
    T *clone();

private:
    void detach_helper();

    T *d;
};

template <class T> inline bool operator==(std::nullptr_t p1, const QSharedDataPointer<T> &p2)
{
    Q_UNUSED(p1);
    return !p2;
}

template <class T> inline bool operator==(const QSharedDataPointer<T> &p1, std::nullptr_t p2)
{
    Q_UNUSED(p2);
    return !p1;
}

template <class T> class QExplicitlySharedDataPointer
{
public:
    typedef T Type;
    typedef T *pointer;

    inline T &operator*() const { return *d; }
    inline T *operator->() { return d; }
    inline T *operator->() const { return d; }
    inline T *data() const { return d; }
    inline const T *constData() const { return d; }
    inline T *take() { T *x = d; d = nullptr; return x; }

    inline void detach() { if (d && d->ref.loadRelaxed() != 1) detach_helper(); }

    inline void reset()
    {
        if(d && !d->ref.deref())
            delete d;

        d = nullptr;
    }

    inline operator bool () const { return d != nullptr; }

    inline bool operator==(const QExplicitlySharedDataPointer<T> &other) const { return d == other.d; }
    inline bool operator!=(const QExplicitlySharedDataPointer<T> &other) const { return d != other.d; }
    inline bool operator==(const T *ptr) const { return d == ptr; }
    inline bool operator!=(const T *ptr) const { return d != ptr; }

    inline QExplicitlySharedDataPointer() { d = nullptr; }
    inline ~QExplicitlySharedDataPointer() { if (d && !d->ref.deref()) delete d; }

    explicit QExplicitlySharedDataPointer(T *data) noexcept;
    inline QExplicitlySharedDataPointer(const QExplicitlySharedDataPointer<T> &o) : d(o.d) { if (d) d->ref.ref(); }

    template<class X>
    inline QExplicitlySharedDataPointer(const QExplicitlySharedDataPointer<X> &o)
#ifdef QT_ENABLE_QEXPLICITLYSHAREDDATAPOINTER_STATICCAST
        : d(static_cast<T *>(o.data()))
#else
        : d(o.data())
#endif
    {
        if(d)
            d->ref.ref();
    }

    inline QExplicitlySharedDataPointer<T> & operator=(const QExplicitlySharedDataPointer<T> &o) {
        if (o.d != d) {
            if (o.d)
                o.d->ref.ref();
            T *old = d;
            d = o.d;
            if (old && !old->ref.deref())
                delete old;
        }
        return *this;
    }
    inline QExplicitlySharedDataPointer &operator=(T *o) {
        if (o != d) {
            if (o)
                o->ref.ref();
            T *old = d;
            d = o;
            if (old && !old->ref.deref())
                delete old;
        }
        return *this;
    }
    inline QExplicitlySharedDataPointer(QExplicitlySharedDataPointer &&o) noexcept : d(o.d) { o.d = nullptr; }
    inline QExplicitlySharedDataPointer<T> &operator=(QExplicitlySharedDataPointer<T> &&other) noexcept
    {
        QExplicitlySharedDataPointer moved(std::move(other));
        swap(moved);
        return *this;
    }

    inline bool operator!() const { return !d; }

    inline void swap(QExplicitlySharedDataPointer &other) noexcept
    { qSwap(d, other.d); }

protected:
    T *clone();

private:
    void detach_helper();

    T *d;
};

template <class T>
Q_INLINE_TEMPLATE QSharedDataPointer<T>::QSharedDataPointer(T *adata) noexcept
    : d(adata)
{ if (d) d->ref.ref(); }

template <class T>
Q_INLINE_TEMPLATE T *QSharedDataPointer<T>::clone()
{
    return new T(*d);
}

template <class T>
Q_OUTOFLINE_TEMPLATE void QSharedDataPointer<T>::detach_helper()
{
    T *x = clone();
    x->ref.ref();
    if (!d->ref.deref())
        delete d;
    d = x;
}

template <class T>
Q_INLINE_TEMPLATE T *QExplicitlySharedDataPointer<T>::clone()
{
    return new T(*d);
}

template <class T>
Q_OUTOFLINE_TEMPLATE void QExplicitlySharedDataPointer<T>::detach_helper()
{
    T *x = clone();
    x->ref.ref();
    if (!d->ref.deref())
        delete d;
    d = x;
}

template <class T>
Q_INLINE_TEMPLATE QExplicitlySharedDataPointer<T>::QExplicitlySharedDataPointer(T *adata) noexcept
    : d(adata)
{ if (d) d->ref.ref(); }

template <class T> inline bool operator==(std::nullptr_t p1, const QExplicitlySharedDataPointer<T> &p2)
{
    Q_UNUSED(p1);
    return !p2;
}

template <class T> inline bool operator==(const QExplicitlySharedDataPointer<T> &p1, std::nullptr_t p2)
{
    Q_UNUSED(p2);
    return !p1;
}

template <class T>
Q_INLINE_TEMPLATE void swap(QSharedDataPointer<T> &p1, QSharedDataPointer<T> &p2)
{ p1.swap(p2); }

template <class T>
Q_INLINE_TEMPLATE void swap(QExplicitlySharedDataPointer<T> &p1, QExplicitlySharedDataPointer<T> &p2)
{ p1.swap(p2); }

template <class T>
Q_INLINE_TEMPLATE uint qHash(const QSharedDataPointer<T> &ptr, uint seed = 0) noexcept
{
    return qHash(ptr.data(), seed);
}
template <class T>
Q_INLINE_TEMPLATE uint qHash(const QExplicitlySharedDataPointer<T> &ptr, uint seed = 0) noexcept
{
    return qHash(ptr.data(), seed);
}

template<typename T> Q_DECLARE_TYPEINFO_BODY(QSharedDataPointer<T>, Q_MOVABLE_TYPE);
template<typename T> Q_DECLARE_TYPEINFO_BODY(QExplicitlySharedDataPointer<T>, Q_MOVABLE_TYPE);

QT_END_NAMESPACE

#endif // QSHAREDDATA_H
相关推荐
用户805533698033 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner3 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz8 天前
QML Hello World 入门示例
qt
xcyxiner11 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner12 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner12 天前
DicomViewer (添加模型类)3
qt
xcyxiner13 天前
DicomViewer (目录调整) 2
qt
xcyxiner13 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00615 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术15 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript