QVariant源码解析

QVariant可以支持任意类型的存储,功能有点类似于boost中的any,但是实现却完全不同,它同样是类型安全的,而且代价更小

要熟悉QVariant,我们需要先看下qRegisterMetaType的源码

先简单看下如何使用QVariant

void CTestVariant::Test()
{
int iNewType = qRegisterMetaType<CVariantHelp>("CVariantHelp");// 先注册一个QT认识的类型


CVariantHelp a1;
QVariant aa(iNewType, &a1);// 告诉QVariant类型及值。这样QVariant就记下了,下次取的时候判断会判断类型,这样实现类型安全


{
QVariant bb = aa;
}


// 取值
int itype = aa.userType();

assert(itype == iNewType);
CVariantHelp* pB =  reinterpret_cast<CVariantHelp*>(aa.data());
}

qRegisterMetaType源码,传入一个T的类型,typename值
template <typename T>
int qRegisterMetaType(const char *typeName)
{
    typedef void*(*ConstructPtr)(const T*); // 定义一个构造函数,接受参数T*的值,返回void*

// 这是一个全局函数,T决定了不同的T会编译出不同的全局函数,cptr保存类型为T的qMetaTypeConstructHelper的全局函数
    ConstructPtr cptr = qMetaTypeConstructHelper<T>; 
    typedef void(*DeletePtr)(T*);
    DeletePtr dptr = qMetaTypeDeleteHelper<T>;
    return QMetaType::registerType(typeName, reinterpret_cast<QMetaType::Destructor>(dptr),
                                   reinterpret_cast<QMetaType::Constructor>(cptr));
}

看下qMetaTypeConstructHelper这个全局函数,比较简单,接受一个T*的值,调用T的默认构造函数(无参数)或则有参数的拷贝构造函数

template <typename T>
void *qMetaTypeConstructHelper(const T *t)
{
    if (!t)
        return new T;
    return new T(*static_cast<const T*>(t));
}

qMetaTypeDeleteHelper类似,是析构函数,代码可以想出来就是delete T了

QMetaType::registerType函数,接受参数typename,T的析构函数,构造函数

并且把构造函数,析构函数都转换成了下面两个类型,还是利用了void*,所以任何类型T的构造函数,析构函数都可以转换成void*这种

typedef void (*Destructor)(void *);
typedef void *(*Constructor)(const void *);

QMetaType::registerType的代码比较复杂,里面有些宏定义,展开也不好跟踪源码,只是根据部分代码推测

Qt定义了一个QT自己内部的所有类型及typename的数组

static const struct { const char * typeName; int type; } types[] = {
    /* All Core types */
    {"void", QMetaType::Void},
    {"bool", QMetaType::Bool},
    {"int", QMetaType::Int},

。。

   {"qreal", QMetaTypeId2<qreal>::MetaType},


    {0, QMetaType::Void}
}

注意,这个里面的是QMetaType,而实际上QVariant也有一个type,这两个类型的值基本是一样的,可能历史原因吧,有两套值

Qt会现在types这个数组里面查找name,是否有,如果没有(最后一个0也是个标志位),再到customeTypes里面找,依然没有找到,就往ct里面append一个,ct就是存储custometypes的vector了

int QMetaType::registerType(const char *typeName, Destructor destructor,
                            Constructor constructor)
{
    QVector<QCustomTypeInfo> *ct = customTypes();

。。。

QWriteLocker locker(customTypesLock());
    int idx = qMetaTypeType_unlocked(normalizedTypeName); // 在types以及customtpye中找typename


    if (!idx) {
        QCustomTypeInfo inf;
        inf.typeName = normalizedTypeName;
        inf.constr = constructor;
        inf.destr = destructor;
        idx = ct->size() + User;
        ct->append(inf); // 如果没有找到,添加一个。ct是类型为QCustomeTpyeInfo的一个QVector.里面需要typename以及它的构造,析构函数
    return idx;

好了,这基本上就是qRegisterMetaType的一个思想了,存储了Qt自带的类型(types这个全局变量的数组(types是static类型的,即内部链接的),自定义类型在custometypes里面。

再往下看QVariant的构造函数,接受一个自定义类型,一个值

有两个重要的变量d.type保存类型,值保存在d.data.shared;中

QVariant::QVariant(int typeOrUserType, const void *copy)
{ create(typeOrUserType, copy); d.is_null = false; }

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

另外也看下默认的两个QT内置类型的构造就更加清晰了

QVariant::QVariant(int val)
{ d.is_null = false; d.type = Int; d.data.i = val; }
QVariant::QVariant(uint val)
{ d.is_null = false; d.type = UInt; d.data.u = val; }

type赋值比较简单,shared的赋值稍微复杂点呢,继续看construct,最后调用到QMetaType了,返回了一个void*

void *QMetaType::construct(int type, const void *copy)
{

...

得到构造函数

const QVector<QCustomTypeInfo> * const ct = customTypes();
        QReadLocker locker(customTypesLock());
        if (type < User || !ct || ct->count() <= type - User)
            return 0;
        if (ct->at(type - User).typeName.isEmpty())
            return 0;
        constr = ct->at(type - User).constr;

调用

return constr(copy); // copy即传入的值,QVariant内部会调用构造函数,通过拷贝构造,保存下用户传入的值

}

然后将void*赋值给shared,x就是上面传入的&d了

void *ptr = QMetaType::construct(x->type, copy);
        if (!ptr) {
            x->type = QVariant::Invalid;
        } else {
            x->is_shared = true;
            x->data.shared = new QVariant::PrivateShared(ptr);
        }

另外此处利用了一个共享指针,其它QVariant使用,不会重新分配新的内存

QVariant bb = aa;的调用过程,只是指针的赋值,然后ref加1

QVariant::QVariant(const QVariant &p)
    : d(p.d)
{
    if (d.is_shared) {
        d.data.shared->ref.ref(); // ref加1
    } else if (p.d.type > Char && p.d.type < QVariant::UserType) {
        handler->construct(&d, p.constData());
        d.is_null = p.d.is_null;
    }
}
析构函数,会将计数减1(defef)并且判断是否为0,为0则调用clear清空共享内存

QVariant::~QVariant()
{
    if ((d.is_shared && !d.data.shared->ref.deref()) || (!d.is_shared && d.type > Char && d.type < UserType))
        handler->clear(&d);
}

这基本上就是QVariant的全部了,总结一下

1、用户自定义需要先注册一个类型,即使用qRegisterMetaType,注册到QT的一个Vector中

2、QVariant里面会new一个用户自定义类型的内存,并调用拷贝构造函数,QVariant自身的赋值会使用共享内存管理

所以用户可以传入一个临时变量地址,如果用户传入的是一个指针,这个指针需要用户自己析构,改变这个指针的值,并不会改变QVariant,因为是两个不同的空间了

而如果QVariant a1=b1(b1是QVariant),改变b1的值会改变a1的。因为这样用的是shared指针

初看2以为是对的,验证发现不准确,改变b1并没有改变a1的值,细看发现这里面有QT使用了个小技巧,要取b1的值然后改变时,会调用data函数

CVariantHelp* pBTemp =  reinterpret_cast<CVariantHelp*>(b1.data());
pBTemp->j_ = 99;

而data的实现会调用detach将shared分离

void* QVariant::data()
{
    detach();
    return const_cast<void *>(constData());
}

void QVariant::detach()
{
    if (!d.is_shared || d.data.shared->ref == 1)
        return;


    Private dd;
    dd.type = d.type;
    handler->construct(&dd, constData());
    if (!d.data.shared->ref.deref())
        handler->clear(&d);
    d.data.shared = dd.data.shared;
}

如果不取用data,两个用一个shared,如果取用,则各用各的,这样节省内存。


你可能感兴趣的:(QVariant源码解析)