本篇从Qt MetaObject源代码解读相关接口的实现,这些接口都定义于qmetaobject.cpp中。

QMetaObject::className()

inline const char *QMetaObject::className() const
{ return d.stringdata; }

从前一篇可知,d.stringdata就是那块字符串数据,包含若干c字符串(以'\0')结尾。如果把d.stringdata当做一个c字符串指针的话,就是这个字符串序列的第一个字符串,正是类名。

QMetaObject::superClass()

inline const QMetaObject *QMetaObject::superClass() const
{ return d.superdata; }

QMetaObject::classInfoCount()

int QMetaObject::classInfoCount() const
{
    int n = priv(d.data)->classInfoCount;
    const QMetaObject *m = d.superdata;
    while (m) {
        n += priv(m->d.data)->classInfoCount;
        m = m->d.superdata;
    }
    return n;
}

从代码可以看出,返回该类的所有classinfo数目,包括所有基类的。

函数priv是一个简单inline函数:

static inline const QMetaObjectPrivate *priv(const uint* data)
{ return reinterpret_cast(data); }

由前一篇可知,d.data指向的是那块二进制信息,priv将d.data解释为QMetaObjectPrivate。

QMetaObjectPrivate是QMetaObject的私有实现类,其数据定义部分如下(见头文件qmetaobject_p.h)。和前一篇的示例moc文件内容一对应,其含义一目了然。
struct QMetaObjectPrivate
{
    int revision;
    int className;
    int classInfoCount, classInfoData;
    int methodCount, methodData;
    int propertyCount, propertyData;
    int enumeratorCount, enumeratorData;
    int constructorCount, constructorData; //since revision 2
    int flags; //since revision 3
    int signalCount; //since revision 
}

QMetaObject:: classInfoOffset ()

int classInfoOffset () const

{

int offset = 0;
    const QMetaObject *m = d.superdata;
    while (m) {
        offset += priv(m->d.data)->classInfoCount;
        m = m->d.superdata;
    }
    return offset;

}

该类的含义是返回这个类所定义的classinfo的起始索引值,相当于它的祖先类所定义的classinfo的数量。

QMetaObject:: classInfo (int index)

QMetaClassInfo classInfo ( int index ) const

{

int i = index;
    i -= classInfoOffset();
    if (i < 0 && d.superdata)
        return d.superdata->classInfo(index);

QMetaClassInfo result;
    if (i >= 0 && i < priv(d.data)->classInfoCount) {
        result.mobj = this;
        result.handle = priv(d.data)->classInfoData + 2*i;
    }
    return result;

}

这个代码的流程比较简单。priv(d.data)->classInfoData是classinfo的信息在d.data中的偏移;每条 classinfo信息占2个UINT的大小,因此“priv(d.data)->classInfoData + 2*i”这个表达式的值就是第i个classinfo的信息在d.data中的偏移。

QMetaObject:: indexOfClassInfo ()

int indexOfClassInfo ( const char * name ) const

{

int i = -1;
    const QMetaObject *m = this;
    while (m && i < 0) {
        for (i = priv(m->d.data)->classInfoCount-1; i >= 0; --i)
            if (strcmp(name, m->d.stringdata
                       + m->d.data[priv(m->d.data)->classInfoData + 2*i]) == 0) {
                i += m->classInfoOffset();
                break;
            }
        m = m->d.superdata;
    }
    return i;

}

按照继承层次,从下往上寻找名字为name的classinfo。

参考前一函数的解释,表达式m->d.data[priv(m->d.data)->classInfoData + 2*i]的值是第i个classinfo信息的第一个32位值。该值是字符信息块d.stringdata中的索引值。因此 m->d.stringdata+ m->d.data[priv(m->d.data)->classInfoData + 2*i]就是classinfo名称的字符串。

int constructorCount () const

int QMetaObject::constructorCount() const
{
    if (priv(d.data)->revision < 2)
        return 0;
    return priv(d.data)->constructorCount;
}

QMetaMethod constructor ( int index ) const

QMetaMethod QMetaObject::constructor(int index) const
{
    int i = index;
    QMetaMethod result;
    if (priv(d.data)->revision >= 2 && i >= 0 && i < priv(d.data)->constructorCount) {
        result.mobj = this;
        result.handle = priv(d.data)->constructorData + 5*i;
    }
    return result;
}

int indexOfConstructor ( const char * constructor ) const

int QMetaObject::indexOfConstructor(const char *constructor) const
{
    if (priv(d.data)->revision < 2)
        return -1;
    for (int i = priv(d.data)->constructorCount-1; i >= 0; --i) {
        if (strcmp(constructor, d.stringdata
                   + d.data[priv(d.data)->constructorData + 5*i]) == 0) {
            return i;
        }
    }
    return -1;
}

int enumeratorCount () const

int enumeratorOffset () const

QMetaEnum enumerator ( int index ) const

int indexOfEnumerator ( const char * name ) const

这组函数与classinfo那一组的实现及其相似。

int methodCount () const 略;
    int methodOffset () const 略;

QMetaMethod method ( int index ) const

{

int i = index;
    i -= methodOffset();
    if (i < 0 && d.superdata)
        return d.superdata->method(index);

QMetaMethod result;
    if (i >= 0 && i < priv(d.data)->methodCount) {
        result.mobj = this;
        result.handle = priv(d.data)->methodData + 5*i;
    }
    return result;

}

该函数的实现方式也一目了然。

int indexOfMethod ( const char * method ) const 略;

int indexOfSignal ( const char * signal ) const

{

const QMetaObject *m = this;
    int i = QMetaObjectPrivate::indexOfSignalRelative(&m, signal);
    if (i >= 0)
        i += m->methodOffset();
    return i;

}

int QMetaObjectPrivate::indexOfSignalRelative(const QMetaObject **baseObject, const char *signal)
{
    int i = -1;
    while (*baseObject) {
        const QMetaObject *const m = *baseObject;
        for (i = priv(m->d.data)->methodCount-1; i >= 0; --i)
            if ((m->d.data[priv(m->d.data)->methodData + 5*i + 4] & MethodTypeMask) == MethodSignal
                && strcmp(signal, m->d.stringdata
                + m->d.data[priv(m->d.data)->methodData + 5*i]) == 0) {
                break;
            }
        if (i >= 0)
            break;
        *baseObject = m->d.superdata;
    }

}

可以看出,查找signal的特别之处在于,通过method元数据的第五项来判断这是不是一个signal。

int indexOfSlot ( const char * slot ) const 略;

int propertyCount () const 略;
int propertyOffset () const 略;

int indexOfProperty ( const char * name ) const 略;

QMetaProperty property ( int index ) const

{

int i = index;
    i -= propertyOffset();
    if (i < 0 && d.superdata)
        return d.superdata->property(index);

QMetaProperty result;
    if (i >= 0 && i < priv(d.data)->propertyCount) {
        int handle = priv(d.data)->propertyData + 3*i;
        int flags = d.data[handle + 2];
        const char *type = d.stringdata + d.data[handle + 1];
        result.mobj = this;
        result.handle = handle;
        result.idx = i;

if (flags & EnumOrFlag) {
            result.menum = enumerator(indexOfEnumerator(type));
            if (!result.menum.isValid()) {
                QByteArray enum_name = type;
                QByteArray scope_name = d.stringdata;
                int s = enum_name.lastIndexOf("::");
                if (s > 0) {
                    scope_name = enum_name.left(s);
                    enum_name = enum_name.mid(s + 2);
                }
                const QMetaObject *scope = 0;
                if (scope_name == "Qt")
                    scope = &QObject::staticQtMetaObject;
                else
                    scope = QMetaObject_findMetaObject(this, scope_name);
                if (scope)
                    result.menum = scope->enumerator(scope->indexOfEnumerator(enum_name));
            }
        }
    }
    return result;

}

该函数的特别之处在于,如果这个propery是一个枚举类型的话,就为返回值QMetaPropery赋上正确QMetaEnum属性值。

QMetaProperty userProperty () const

{

const int propCount = propertyCount();
    for (int i = propCount - 1; i >= 0; --i) {
        const QMetaProperty prop = property(i);
        if (prop.isUser())
            return prop;
    }
    return QMetaProperty();

}

从这个函数的实现来看,一个QObject应该只会有一个打开USER flag的property。

Qt MetaObject sysmtem 详解之三:QMetaObject接口实现相关推荐

  1. QtMetaObjectsysmtem详解之三:QMetaObject接口实现

    本篇从Qt MetaObject源代码解读相关接口的实现,这些接口都定义于qmetaobject.cpp中. QMetaObject::className() inline const char *Q ...

  2. QT Echarts 使用详解(一)ECharts下载\示例\动态缩放

    Echarts是百度的一款可视化界面开发的平台,里面的地图以及数据可视化内容十分丰富,适合用于一些大屏数据显示项目或者一些ui界面开发.每一个ECharts图表使用一个无边框的QWebView来展示, ...

  3. 服务器和网页接口,WebApi架构详解,WebApi接口搭建与部署WebApi服务器

      WebApi架构详解,WebApi接口搭建与部署WebApi服务器 本文关键词:WebApi架构, WebApi接口搭建, WebApi部署 1. Api是什么? API(Application ...

  4. QT QtableView操作详解

    本文实现了使用QtableView控件来显示数据,数据源使用txt文本作为数据源,使用了QStandardItemModel作为数据模型来实现了对TableView空间的初始化,和对txt数据源的增删 ...

  5. Qt pro 文件详解

     Qt pro 文件详解 1. TEMPLATE  变量TEMPLATE描述了为建立目标文件而采用何种模板,即生成何种形式的Makefile文件.Qmake  工具定义了5种模板:   a. 应用 ...

  6. Qt 5.12--Item详解

    Qt 5.12--Item详解 1 作为容器 2 默认属性 3 透明度 4 堆叠顺序 5 定位子项目和坐标映射 参考 QML中的所有可视项目都继承自Item.虽然Item本身没有可视化的外观,但是它定 ...

  7. Fragment详解之三——管理Fragment(1)

    前言:follow your heart,be your own king 相关文章: 1.<Fragment详解之一--概述> 2.<Fragment详解之二--基本使用方法> ...

  8. java callable 详解_详解Java Callable接口实现多线程的方式

    在Java 1.5以前,创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口.无论我们以怎样的形式实现多线程,都需要调用Thread类中的start方法去向操作系统请求 ...

  9. [QT] QMap使用详解

    [QT] QMap使用详解 引言 ​ QMap/QMultiMap属于关联式容器,其底层结构是通过二叉树实现,故其查找value的效率很快.QMap中的数据都是成对出现的,第一个称为key(键),第二 ...

最新文章

  1. iOS开发之--TableViewCell重用机制避免重复显示问题
  2. 运用js动态操作table(新增,删除相关列信息)
  3. 几步实现stm32上面移植mqtt
  4. window.print只打印了1页的原因
  5. SAP Spartacus cxFocus增添了refresh Focus功能后的一些考虑
  6. C# 小闹钟 v3.0
  7. 如何将汇编语言转换为c语言,如何把汇编语言转换成C语言
  8. Flash MX 认证考试(样题)
  9. php表决器代码,adder3 此源代码是基于Verilog语言的七人投票表决器 、2 个 联合开发网 - pudn.com...
  10. AD9在PCB中放大元件后,元件表面出现网格
  11. 高中计算机学业水平考试教程,高中学业水平考试信息技术操作题步骤
  12. 用logisim实现串行的全加器
  13. 前端工程师的价值体现在哪里?
  14. MaxProxy可以成为永久关闭的911S5代理的新选择吗?
  15. 论文复现-1论文重读:Black-Box Tuning for Language-Model-as-a-Service
  16. 4.外部协作单位的组织与管理
  17. 使用howler做一个音频播放器到底有多简单
  18. C#开发技术 计算器(二进制、八进制、十进制)
  19. 企业要在云优先的趋势下,制定符合业务发展的云计算解决方案
  20. 一键修改计算机名-加域-加组脚本编写历程

热门文章

  1. i3 1005g1和i5 10210u哪个好
  2. RK3399 Android7.1修改系统默认壁纸
  3. preempt-RT patches
  4. 华为交换机端口安全配置
  5. CONDITION EVALUATION DELTA
  6. 浅谈百度闪电算法以及网站HTTPS(图文)
  7. Python数据分析第八课:初识Matplotlib
  8. U盘启动安装CentOS Linux系统
  9. 中国顶尖“黑客”团队:一半是历年高考状元
  10. 数字图像 - 图像隐写