QVariant是如何存储数据的

QVariant这个类可以说是一个万能数据类型存储对象类,我们可以使用这个类型存储我们需要的任何数据。那么它到底是如何存储数据的呢?

查看QVariant头文件,可以发现对于比较常见的类型例如int、bool、QString等,QVariant提供了对应的构造函数,我们可以直接调用构造函数来把数据存储到一个QVariant变量中:

int num = 10;
QVariant varInt(num);

QString str = "qwer";
QVariant varQString(str);

以int类型和QString类型为例,二者对应的QVariant构造函数实现如下:

QVariant::QVariant(int val)
    : d(Int)
{ d.data.i = val; }

先看int类型对应的构造函数,可以看到只对成员变量d进行了操作。查看d的定义,发现d为Private类型,而Private的定义如下:

在这里插入图片描述
在这里插入图片描述

其中,union联合体Data用于保存数据,包含基本类型和QObject *、void *、PrivateShared *;type用于存储数据的类型,is_shared表示数据是否共享,is_null表示数据是否为空。

那么int类型对应的QVariant构造函数就把d.type即存储的数据类型设置为了Int,同时直接把int值保存到d.data.i联合体中。

下面再看QString类型对应的构造函数:

QVariant::QVariant(const QString &val)
    : d(String)
{ v_construct<QString>(&d, val);  }

可以发现除了给d.type赋值为String外,还调用了v_construct函数。对源代码追溯如下:

template <class T>
inline void v_construct(QVariant::Private *x, const T &t)
{
    // dispatch
    v_construct_helper(x, t, typename QVariantIntegrator<T>::CanUseInternalSpace_t());
}

template<typename T>
struct QVariantIntegrator
{
    static const bool CanUseInternalSpace = sizeof(T) <= sizeof(QVariant::Private::Data)
            && ((QTypeInfoQuery<T>::isRelocatable) || std::is_enum<T>::value);
    typedef std::integral_constant<bool, CanUseInternalSpace> CanUseInternalSpace_t;
};

template <class T>
inline void v_construct_helper(QVariant::Private *x, const T &t, std::true_type)
{
    new (&x->data) T(t);
    x->is_shared = false;
}

template <class T>
inline void v_construct_helper(QVariant::Private *x, const T &t, std::false_type)
{
    x->data.shared = new QVariantPrivateSharedEx<T>(t);
    x->is_shared = true;
}

typename QVariantIntegrator::CanUseInternalSpace_t()在这的作用主要是判断存储值的大小是否超过了联合体Data的大小,对于QString而言,sizeof(QString) <= sizeof(QVariant::Private::Data),直接在QVariant::d.data位置就地构造,d.is_shared置为false。而对于大小超过联合体的,就需要保存到QVariant::d.data.shared。构造完成后的QVariant变量如下所示:

在这里插入图片描述

对于自定义类型和指针类型,一般需要用到fromValue:

struct Custom 
{
    double num = 10.0;
    std::string str = "asdf";
};
Q_DECLARE_METATYPE(Custom);
Q_DECLARE_METATYPE(Custom *);

Custom custom;
QVariant varCustom = QVariant::fromValue<Custom>(custom);

Custom *pCustom;
QVariant varPtrCustom = QVariant::fromValue<Custom *>(pCustom);

源代码追溯如下:

template<typename T>
static inline QVariant fromValue(const T &value)
{
    return qVariantFromValue(value);
}

template <typename T>
inline QVariant qVariantFromValue(const T &t)
{
    return QVariant(qMetaTypeId<T>(), &t, QTypeInfo<T>::isPointer);
}

QVariant::QVariant(int typeId, const void *copy, uint flags)
{
    /* 判断该数据是否是指针类型 */
    if (flags)
    {
        d.type = typeId;
        d.data.ptr = *reinterpret_cast<void *const*>(copy);
    }
    else
    {
        create(typeId, copy);
    }
    d.is_null = false;
}

这里可以看到QVariant在存储数据时,将普通数据和指针区分开了,对于指针类型的数据,直接存储到d.data.ptr;对于其他数据,查看create接口实现:

void QVariant::create(int type, const void *copy)
{
    d.type = type;
    handlerManager[type]->construct(&d, copy);
}

设置了d.type之后,调用了construnct函数,经查阅发现handlerManager存储的对象如下:

const QVariant::Handler qt_kernel_variant_handler = {
    construct,
    clear,
    isNull,
#ifndef QT_NO_DATASTREAM
    0,
    0,
#endif
    compare,
    convert,
    0,
#if !defined(QT_NO_DEBUG_STREAM)
    streamDebug
#else
    0
#endif
};

qt_kernel_variant_handler结构里面其实存储了数据的构造函数、清理函数、转换函数等等,这些都是预先定义好的函数指针,我们目前所关心的是construct接口,看看construct函数长什么样:

static void construct(QVariant::Private *x, const void *copy)
{
    QVariantConstructor<CoreTypesFilter> constructor(x, copy);
    QMetaTypeSwitcher::switcher<void>(constructor, x->type, 0);
}

先看QVariantConstructor的构造函数:

QVariantConstructor(QVariant::Private *x, const void *copy)
        : m_x(x)
        , m_copy(copy)
{}

只是单纯给成员变量赋值。接着看QMetaTypeSwitcher::switcher<void>(constructor, x->type, 0)干了什么

template<class ReturnType, class DelegateObject>
ReturnType QMetaTypeSwitcher::switcher(DelegateObject &logic, int type, const void *data)
{
    switch (QMetaType::Type(type)) {
    QT_FOR_EACH_STATIC_TYPE(QT_METATYPE_SWICHER_CASE)

    case QMetaType::UnknownType:
        return logic.delegate(static_cast<UnknownType const *>(data));
    default:
        if (type < QMetaType::User)
            return logic.delegate(static_cast<UnknownType const *>(data));
        return logic.delegate(static_cast<NotBuiltinType const *>(data));
    }
}

可以看到ReturnType的类型是voidDelegateObject的类型是QVariantConstructordata也就是我们最终存储的数据

QT_FOR_EACH_STATIC_TYPE这个宏,将所有数据过一遍:

#define QT_METATYPE_SWICHER_CASE(TypeName, TypeId, Name)\
    case QMetaType::TypeName: return logic.delegate(static_cast<Name const *>(data));

// F is a tuple: (QMetaType::TypeName, QMetaType::TypeNameID, RealType)
// ### Qt6: reorder the types to match the C++ integral type ranking
#define QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(F)\
    F(Void, 43, void) \
    F(Bool, 1, bool) \
    F(Int, 2, int) \
    F(UInt, 3, uint) \
    F(LongLong, 4, qlonglong) \
    F(ULongLong, 5, qulonglong) \
    F(Double, 6, double) \
    F(Long, 32, long) \
    F(Short, 33, short) \
    F(Char, 34, char) \
    F(ULong, 35, ulong) \
    F(UShort, 36, ushort) \
    F(UChar, 37, uchar) \
    F(Float, 38, float) \
    F(SChar, 40, signed char) \
    F(Nullptr, 51, std::nullptr_t) \
    F(QCborSimpleType, 52, QCborSimpleType) \

继续看delegate是个什么,也就是 QVariantConstructor::delegate,可以猜测它根据各种不同的数据类型实现了不同的数据类型特化版本

template<typename T>
void delegate(const T*)
{
    FilteredConstructor<T>(*this);
}

template<typename T, bool IsAcceptedType = Filter::template Acceptor<T>::IsAccepted>
struct FilteredConstructor
{
    FilteredConstructor(const QVariantConstructor &tc)
    {
        v_construct<T>(tc.m_x, tc.m_copy);
        tc.m_x->is_null = !tc.m_copy;
    }
};

// constructs a new variant if copy is 0, otherwise copy-constructs
template <class T>
inline void v_construct(QVariant::Private *x, const void *copy, T * = 0)
{
    if (copy)
        v_construct<T>(x, *static_cast<const T *>(copy));
    else
        v_construct_helper<T>(x, typename QVariantIntegrator<T>::CanUseInternalSpace_t());
}

template <class T>
inline void v_construct_helper(QVariant::Private *x, std::false_type)
{
    //此处将is_shared值设置为true
    x->data.shared = new QVariantPrivateSharedEx<T>;
    x->is_shared = true;
}

//普通数据类型
void delegate(const void*)
{
    qWarning("Trying to create a QVariant instance of QMetaType::Void type, an invalid QVariant will be constructed instead");
    m_x->type = QMetaType::UnknownType;
    m_x->is_shared = false;
    m_x->is_null = !m_copy;
}

//位置类型,这个不知道是什么类型
void delegate(const QMetaTypeSwitcher::UnknownType*)
{
    if (m_x->type != QMetaType::UnknownType) {
        qWarning("Trying to construct an instance of an invalid type, type id: %i", m_x->type);
        m_x->type = QMetaType::UnknownType;
    }
    m_x->is_shared = false;
    m_x->is_null = !m_copy;
}

//可以看到无论哪一种处理,都是对QVariantConstructor类型的补充处理

总结:

1、对于基本数据类型int、bool等,直接存储到union中

2、对于指针类型数据,直接存储到QVariant::d.data.ptr中

3、对于Qt内置类型和自定义类型,大小不超过联合体的,直接在QVariant::d.data位置就地构造;大小超过联合体的,保存到QVariant::d.data.shared

  • 30
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值