QT6 源(50):阅读源码 struct QArrayDataPointer,本类用于实现 QString 。本类中实现了对容器中内部数组的管理

(1)
在这里插入图片描述

(2)来自于头文件 qarraydatapointer . h

#ifndef QARRAYDATAPOINTER_H
#define QARRAYDATAPOINTER_H

#include <QtCore/qarraydataops.h>
#include <QtCore/qcontainertools_impl.h>

QT_BEGIN_NAMESPACE //说明本类定义于 QT 的全局命名空间

/*
struct QArrayData
{   QBasicAtomicInt ref_ ;
    ArrayOptions    flags;
    qsizetype       alloc;

    static void *   allocate(...) noexcept;
    static void   deallocate(...) noexcept;
};

template <class T>
struct QTypedArrayData : QArrayData {  }; //本类无数据成员

template <class T>
struct QArrayDataPointer //上面的注释给出了学习与阅读本头文件的因缘
{
    Data * d  ; //Data = QTypedArrayData<T> 包含了申请释放内存的成员方法
    T    * ptr; //真正的堆区中的数据起点
    qsizetype size;
};

class QString
{   typedef QTypedArrayData<char16_t> Data; //一个类型定义

    DataPointer d;
  //DataPointer = QStringPrivate = QArrayDataPointer<char16_t>;
};
*/

template <class T>
struct QArrayDataPointer //上面的注释给出了学习与阅读本头文件的因缘
{
private:
    typedef QTypedArrayData<T> Data   ;
    typedef QArrayDataOps<T>   DataOps;
   //template <class T>
   //struct QArrayDataOps : QtPrivate::QCommonArrayOps<T> { };

public:
    Data * d  ; // d 实现了对内存的申请与释放
    T    * ptr; // 指向堆区中的存放数据的数组的起始地址
    qsizetype size;

    enum {
        pass_parameter_by_value = std::is_arithmetic<T>::value ||
                                  std::is_pointer<T>   ::value ||
                                  std::is_enum<T>      ::value
    };

    typedef typename std::conditional<pass_parameter_by_value,
                   T, const T &>::type parameter_type;
    //上面是分析值传递还是引用传递。简单情况下将是值传递。


    constexpr QArrayDataPointer() noexcept               //默认构造函数
        : d(nullptr), ptr(nullptr), size(0) { }

    constexpr QArrayDataPointer(Data *header, T *adata, qsizetype n = 0) noexcept
        : d(header), ptr(adata), size(n) {  }            //有参构造函数

    explicit QArrayDataPointer(QPair<QTypedArrayData<T> *, T *> adata,
                               qsizetype n = 0) noexcept //有参构造函数
        : d(adata.first), ptr(adata.second), size(n) {  }

    QArrayDataPointer(const QArrayDataPointer &other) noexcept // copy构造函数
        : d(other.d), ptr(other.ptr), size(other.size) { ref(); }

    QArrayDataPointer(QArrayDataPointer &&other) noexcept      //移动构造函数
        : d(other.d), ptr(other.ptr), size(other.size)
    {
        other.d = nullptr;
        other.ptr = nullptr;
        other.size = 0;
    }
                                                              //copy赋值运算符函数
    QArrayDataPointer & operator=(const QArrayDataPointer & other) noexcept
    {
        QArrayDataPointer tmp(other);
        this->swap(tmp);
        return *this;
    }

    QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QArrayDataPointer)
/*                                  以宏定义的形式给出的 移动赋值运算符函数
#define QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(Class) \
    Class &operator=(Class &&other) noexcept { \
        Class moved(std::move(other)); \
        swap(moved); \
        return *this; \
    }
*/

    ~QArrayDataPointer() //析构函数
    {
        if (!deref()) //deref():Returns false if deallocation is necessary
        {             //如果需要释放内存,则返回 false
            (*this)->destroyAll();

            Data::deallocate(d);
        }
    }


    //本静态函数,根据裸数据返回 QArrayDataPointer 对象,值传递
    static QArrayDataPointer fromRawData(const T *rawData, qsizetype length) noexcept
    {
        Q_ASSERT(rawData || !length);

        return { nullptr, const_cast<T *>(rawData), length };
    }

    //template <class T>
    //struct QArrayDataOps : QtPrivate::QCommonArrayOps<T> { };
          DataOps & operator*()        noexcept
    {
        return *static_cast<      DataOps *>(this);
    }

    const DataOps & operator*()  const noexcept
    {
        return *static_cast<const DataOps *>(this);
    }

          DataOps * operator->()       noexcept
    {
        return static_cast<DataOps *>(this);
    }

    const DataOps * operator->() const noexcept
    {
        return static_cast<const DataOps *>(this);
    }

          T * data()       noexcept { return ptr; } //返回封装的裸数组
    const T * data() const noexcept { return ptr; }

          T *      begin()       noexcept { return data(); } //调用了上面的 data()函数
    const T *      begin() const noexcept { return data(); } //返回的是裸指针
    const T * constBegin() const noexcept { return data(); } //返回的不是迭代器

          T *        end()       noexcept { return data() + size; }
    const T *        end() const noexcept { return data() + size; }
    const T *   constEnd() const noexcept { return data() + size; }

    bool isNull   () const noexcept { return !ptr;  } //无注释。有数组则不空
    bool isMutable() const noexcept { return d; } // 有d 成员就是可修改的
    bool isShared () const noexcept { return !d || d->isShared(); }
                                    //只要 d 在共享,就是共享的
    bool isSharedWith(const QArrayDataPointer & other) const noexcept
    { return d && d == other.d; }  //判断本对象是否在与形参对象共享

    Data * d_ptr  ()       noexcept { return d; } //返回的是 d 的裸指针形式,d 的 ptr
    void setBegin(T * begin) noexcept { ptr = begin; } //设置裸指针

    void   ref() noexcept {       if (d) d->ref()  ; } //若存在共享则返回 true
    bool deref() noexcept { return !d || d->deref(); } //如果需要释放内存,则返回 false

    // forwards from QArrayData
    qsizetype      allocatedCapacity()       noexcept
    { return d ? d->allocatedCapacity() : 0; } //好像是查询已经分配的内存容量值
/*
struct QArrayData
{   QBasicAtomicInt ref_ ;
    ArrayOptions    flags;
    qsizetype       alloc;

    qsizetype allocatedCapacity() { return alloc; }
};
*/
    qsizetype constAllocatedCapacity() const noexcept
    { return d ? d->constAllocatedCapacity() : 0; }

    bool needsDetach() const noexcept //如果需要在修改数据之前进行分离,则返回 true。
    { return !d || d->needsDetach(); }

    qsizetype detachCapacity(qsizetype newSize) const noexcept
    { return d ? d->detachCapacity(newSize) : newSize; } //猜测为需要释放的内存字节数

    //枚举值 ArrayOptionDefault = 0, 与  CapacityReserved = 1
    const typename Data::ArrayOptions flags() const noexcept
    { return d ? d->flags : Data::ArrayOptionDefault; }

    void   setFlag(typename Data::ArrayOptions f) noexcept
    { Q_ASSERT(d); d->flags |= f; }

    void clearFlag(typename Data::ArrayOptions f) noexcept
    { if (d) d->flags &= ~f; }

    void swap(QArrayDataPointer &other) noexcept //与形参交换数据
    {
        qt_ptr_swap(d, other.d);
        qt_ptr_swap(ptr, other.ptr);
        std::swap(size, other.size);
    }

    void clear() noexcept(std::is_nothrow_destructible<T>::value)
    {
        QArrayDataPointer tmp;                  //清除本数组中的数据
        swap(tmp);
    }

    void detach(QArrayDataPointer * old = nullptr)
    {
        if (needsDetach())
            reallocateAndGrow(QArrayData::GrowsAtEnd, 0, old);
    }

    /*! \internal

        Detaches this (optionally) and grows to accommodate the free space for
        \a n elements at the required side. The side is determined from \a pos.

        \a data pointer can be provided when the caller knows that \a data
        points into range [this->begin(), this->end()). In case it is, *data
        would be updated so that it continues to point to the element it was
        pointing to before the data move. if \a data does not point into range,
        one can/should pass \c nullptr.

        Similarly to \a data, \a old, pointer to a default-constructed QADP, can
        be provided when the caller expects to e.g. copy the data from this to
        itself:
        \code
        QList<T> list(5);
        qsizetype pos = getArbitraryPos();
        list.insert(pos, list.begin(), list.end());
        \endcode

        The default rule would be: \a data and \a old must either both be valid
        pointers, or both equal to \c nullptr.
    */
    void detachAndGrow(QArrayData::GrowthPosition where,
                       qsizetype n, const T **data,
                       QArrayDataPointer *old)
    {
        const bool detach = needsDetach();
        bool readjusted = false;
        if (!detach)
        {
            if (!n
                || (where == QArrayData::GrowsAtBeginning && freeSpaceAtBegin() >= n)
                || (where == QArrayData::GrowsAtEnd && freeSpaceAtEnd() >= n) )
                return;

            readjusted = tryReadjustFreeSpace(where, n, data);

            Q_ASSERT(!readjusted
                || (where == QArrayData::GrowsAtBeginning && freeSpaceAtBegin() >= n)
                || (where == QArrayData::GrowsAtEnd && freeSpaceAtEnd() >= n));
        }

        if (!readjusted)
            reallocateAndGrow(where, n, old);
    }

    /*! \internal

        Reallocates to accommodate the free space for \a n elements at the
        required side. The side is determined from \a pos. Might also shrink
        when n < 0.
    */
    Q_NEVER_INLINE void reallocateAndGrow(QArrayData::GrowthPosition where,
                                          qsizetype n,
                                          QArrayDataPointer *old = nullptr)
    {
        if constexpr (QTypeInfo<T>::isRelocatable &&
                        alignof(T) <= alignof(std::max_align_t))
        {
            if (where == QArrayData::GrowsAtEnd && !old && !needsDetach() && n > 0)
            {
                (*this)->reallocate(constAllocatedCapacity() - freeSpaceAtEnd() + n,
                                    QArrayData::Grow); // fast path
                return;
            }
        }

        QArrayDataPointer dp(allocateGrow(*this, n, where));

        if (n > 0)
            Q_CHECK_PTR(dp.data());

        if (where == QArrayData::GrowsAtBeginning) {
            Q_ASSERT(dp.freeSpaceAtBegin() >= n);
        } else {
            Q_ASSERT(dp.freeSpaceAtEnd() >= n);
        }

        if (size) {
            qsizetype toCopy = size;

            if (n < 0)     toCopy += n;

            if (needsDetach() || old)
                dp->copyAppend(begin(), begin() + toCopy);
            else
                dp->moveAppend(begin(), begin() + toCopy);

            Q_ASSERT(dp.size == toCopy);
        }

        swap(dp);

        if (old)      old->swap(dp);
    }

    /*! \internal

        Attempts to relocate [begin(), end()) to accommodate the free space for
        \a n elements at the required side. The side is determined from \a pos.

        Returns \c true if the internal data is moved. Returns \c false when
        there is no point in moving the data or the move is impossible. If \c
        false is returned, it is the responsibility of the caller to figure out
        how to accommodate the free space for \a n elements at \a pos.

        This function expects that certain preconditions are met, e.g. the
        detach is not needed, n > 0 and so on. This is intentional to reduce the
        number of if-statements when the caller knows that preconditions would
        be satisfied.

        \sa reallocateAndGrow
    */
    bool tryReadjustFreeSpace(QArrayData::GrowthPosition pos,
                              qsizetype n, const T **data = nullptr)
    {
        Q_ASSERT(!this->needsDetach());
        Q_ASSERT(n > 0);

        Q_ASSERT((pos == QArrayData::GrowsAtEnd && this->freeSpaceAtEnd() < n)
      || (pos == QArrayData::GrowsAtBeginning && this->freeSpaceAtBegin() < n) );

        const qsizetype capacity = this->constAllocatedCapacity();
        const qsizetype freeAtBegin = this->freeSpaceAtBegin();
        const qsizetype freeAtEnd = this->freeSpaceAtEnd();

        qsizetype dataStartOffset = 0;
        // algorithm:
        //   a. GrowsAtEnd: relocate if space at begin AND size < (capacity * 2) / 3
        //      [all goes to free space at end]:
        //      new free space at begin = 0
        //
        //   b. GrowsAtBeginning: relocate if space at end AND size < capacity / 3
        //      [balance the free space]:
        //      new free space at begin = n + (total free space - n) / 2
        if (pos == QArrayData::GrowsAtEnd && freeAtBegin >= n
            && ((3 * this->size) < (2 * capacity))) {
            // dataStartOffset = 0; - done in declaration
        } else if (pos == QArrayData::GrowsAtBeginning && freeAtEnd >= n
                   && ((3 * this->size) < capacity)) {
            // total free space == capacity - size
            dataStartOffset = n + qMax(0, (capacity - this->size - n) / 2);
        } else {
            // nothing to do otherwise
            return false;
        }

        relocate(dataStartOffset - freeAtBegin, data);

        Q_ASSERT((pos == QArrayData::GrowsAtEnd && this->freeSpaceAtEnd() >= n)
      || (pos == QArrayData::GrowsAtBeginning && this->freeSpaceAtBegin() >= n) );
        return true;
    }

    /*! \internal

        Relocates [begin(), end()) by \a offset and updates \a data if it is not
        \c nullptr and points into [begin(), end()).
    */
    void relocate(qsizetype offset, const T **data = nullptr)
    {
        T *res = this->ptr + offset;

        QtPrivate::q_relocate_overlap_n(this->ptr, this->size, res);

        // first update data pointer, then this->ptr
        if (data && QtPrivate::q_points_into_range(*data, this->begin(), this->end()))
            *data += offset;

        this->ptr = res;
    }

    qsizetype freeSpaceAtBegin() const noexcept
    {
        if (d == nullptr)
            return 0;

        return this->ptr - Data::dataStart(d, alignof(typename Data::AlignmentDummy));
    }

    qsizetype freeSpaceAtEnd() const noexcept
    {
        if (d == nullptr)
            return 0;

        return d->constAllocatedCapacity() - freeSpaceAtBegin() - this->size;
    }

    // allocate and grow. 
    //Ensure that at the minimum requiredSpace is available at the requested end
    static QArrayDataPointer allocateGrow(const QArrayDataPointer &from,
                          qsizetype n, QArrayData::GrowthPosition position)
    {
        // calculate new capacity. We keep the free capacity at the side that
        //does not have to grow
        // to avoid quadratic behavior with mixed append/prepend cases

        // use qMax below, because constAllocatedCapacity() can be 0 when
        //using fromRawData()
        qsizetype minimalCapacity = qMax(from.size, from.constAllocatedCapacity()) + n;
        // subtract the free space at the side we want to allocate.
        //This ensures that the total size requested is
        // the existing allocation at the other side + size + n.
        minimalCapacity -= (position == QArrayData::GrowsAtEnd)
                               ? from.freeSpaceAtEnd()
                               : from.freeSpaceAtBegin();

        qsizetype capacity = from.detachCapacity(minimalCapacity);
        const bool   grows = capacity > from.constAllocatedCapacity();
        auto [header, dataPtr] = Data::allocate(capacity, grows
                                    ? QArrayData::Grow
                                    : QArrayData::KeepSize);

        const bool valid = header != nullptr && dataPtr != nullptr;
        if (!valid)
            return QArrayDataPointer(header, dataPtr);

        // Idea: * when growing backwards,
        //adjust pointer to prepare free space at the beginning
        //       * when growing forward, adjust by the previous data pointer offset
        dataPtr += (position == QArrayData::GrowsAtBeginning)
                        ? n + qMax(0, (header->alloc - from.size - n) / 2)
                        : from.freeSpaceAtBegin();
        header->flags = from.flags();
        return QArrayDataPointer(header, dataPtr);
    }

    friend bool operator==(const QArrayDataPointer &lhs, //封装的数据完全相同
                           const QArrayDataPointer &rhs) noexcept
    {
        return lhs.data() == rhs.data() && lhs.size == rhs.size;
    }

    friend bool operator!=(const QArrayDataPointer &lhs,//数组起点或长度不一样就为不同
                           const QArrayDataPointer &rhs) noexcept
    {
        return lhs.data() != rhs.data() || lhs.size != rhs.size;
    }

}; //完结 struct QArrayDataPointer

template <class T> //实现了重载函数,支持全局版本的互换操作
inline void qSwap(QArrayDataPointer<T> &p1, QArrayDataPointer<T> &p2) noexcept
{
    p1.swap(p2);
}


//  Q_ARRAY_LITERAL

// The idea here is to place a (read-only) copy of header and array data in an
// mmappable portion of the executable (typically, .rodata section).

// Hide array inside a lambda
#define Q_ARRAY_LITERAL(Type, ...) \
    ([]() -> QArrayDataPointer<Type> { \
        static Type const data[] = { __VA_ARGS__ }; \
        return QArrayDataPointer<Type>::\
        		 fromRawData(const_cast<Type *>(data), std::size(data)); \
    }())
/**/

QT_END_NAMESPACE

#endif // include guard

(3)

谢谢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值