c++ - QMetaObject::indexOfMethod 返回的索引到底是什么?

标签 c++ qt properties metadata

为了详细说明模棱两可的标题,我想大致了解元系统是如何动态工作的。

在静态上下文中使用插槽/属性访问器时,调用可能是内联的,毕竟如果有可能为什么不呢?

但是动态场景和查询索引呢?元对象是如何实现的?偏移量是虚拟表中的指针之一吗?或者 Qt 可能会创建自己的 vtable,而不是类虚拟方法使用的 vtable?在这种情况下,虚拟属性方法是否在类“ native ”vtable 以及为静态元对象创建的假设的额外方法中重复?实际通话在技术上是虚拟的吗?

我对复杂的细节不感兴趣,更喜欢整体概念。

最佳答案

首先,就可移植 C++ 而言,没有“虚拟表”之类的东西。这是一个实现细节,被编译器隐藏。没有办法可移植地访问它的内部(实现的数据结构),只能访问它的语义(它提供的功能)。

其次,您没有说出“通话”的含义。假设我们有

class BaseObject : public QObject {
  Q_OBJECT
public:
  Q_SIGNAL void mySignal();
}

class MyObject : public BaseObject {
  Q_OBJECT
public:
  Q_SLOT void mySlot();
};
MyObject myObject;

有多种调用方式mySlot .
  • 直接调用它:
    myObject.mySlot("yay!")
    

    这与调用任何其他方法没有什么不同——仅仅因为它是一个插槽,从 C++ 的角度来看,它并没有什么特别之处。如果它碰巧是一个虚拟方法,它就是一个虚拟方法调用,在给定平台上会产生任何开销。
  • 通过使用虚拟 qt_metacall带有方法索引的方法:
    myObject.qt_metacall(QMetaObject::InvokeMetaMethod, 4, nullptr);
    
    qt_metacall的执行由 moc 生成。 qt_metacall 是定义方法索引的地方 .在内部,qt_metacall一直递归调用自身到 QObject::qt_metacall .

    每个实现都会检查方法索引是否小于此类的元方法数。带有此信息的常量数据记录由 moc 生成。例如,QObject有三个元方法——两个信号和一个槽。如果索引大于 2,则按元方法的数量递减,并返回到下一个派生类的 qt_metacall .

    QObject::qt_metacall返回 BaseObject::qt_metacall ,索引已减 3,现在为 1 (4-3 = 1)。由于BaseObject只有一个元方法(索引 0),该索引减 1 并返回。

    BaseObject::qt_metacall返回 MyObject::qt_metacall ,索引已减少 (3+1=4),现在为零 (0)。那是唯一的本地索引mySlot , 并通过将索引传递给 MyObject::qt_static_metacall 来处理调用.
  • 通过使用静态 qt_static_metacall (不过,这是一种私有(private)方法):
    MyObject::qt_static_metacall(&myObject, QMetaObject::InvokeMethod, 0, nullptr);
    
    qt_static_metacall是实现实际调用的静态方法。它只是打开本地的、基于 0 的索引,并调用该方法,传递它需要的任何参数。指向参数的指针在最后一个参数中传递 - 这里只是一个 nullptr因为没有论据。这是普通无聊的 C++ 代码,没有什么魔力。

    我们使用索引为 4 的方法实际上是 MyObject 上的方法这一知识。 (而不是,例如 QObjectBaseObject )。由于所有基类一起使用了 4 个索引,我们将方法索引向下调整相同的数量 - 到零(4-4 = 0)。

    所以,如果知 Prop 体类实现了方法索引,就可以直接调用静态方法,不用递归虚qt_metacall .此查找由 QObject::connect 执行建立连接时。连接的目标存储为本地方法索引和指向 qt_static_metacall 的指针。具有给定方法的类的方法。这节省了 qt_metacall 的递归成本当连接信号调用槽时。
  • 通过使用 QMetaObject::invokeMethod :
    QMetaObject::invokeMethod(&myObject, "mySlot");
    

    这将执行与 QObject::connect 相同的所有查找。确实如此,但不是建立连接,而是立即执行调用。同样,它将以 MyObject::qt_static_metacall 结束。 .
  • 通过使用 QMetaMethod::invoke :
    QMetaMethod method = myObject.metaObject()->method(
                           myObject.metaObject->indexOfSlot("mySlot()"));
    method.invoke(myObject);
    
    QMetaObject将查找到的指针缓存到 MyObject::qt_static_metacall以及本地方法索引 0。因此 invoke调用的开销比来自 QMetaObject 的调用少.
  • 关于c++ - QMetaObject::indexOfMethod 返回的索引到底是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21365972/

    相关文章:

    ios - 多个属性的相同 didSet block

    c# - EF 6 数据库首先在生成的实体类中使所有属性虚拟化

    c++ - Qt线程同步

    node.js - 如何在 Node js中定义属性文件?

    c++ - 在 Debug模式下执行控制台应用程序后,如何让 Visual Studio 暂停?

    C++指针分配char数组的地址

    c++ - Qt 多线程信号和槽行为的问题

    c++ - Qt中处理 "events"

    c++ - 寻找数组中的平衡点

    c++ - 引用类的静态成员导致 "not a static member of class"