首页 QObject体系对象数据系统
文章
取消

QObject体系对象数据系统

首先,给定一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
class MyClass : public QObject
{
    Q_OBJECT // 这个宏告诉MOC这个类要使用元对象系统

public:
    MyClass(QObject *parent = nullptr);

signals:
    void mySignal(int value); // 定义一个带有一个int参数的信号

public slots:
    void mySlot(int value); // 定义一个带有一个int参数的槽
};

Q_OBJECT 这个宏是Qt元对象的核心,查看它的展开如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#define Q_OBJECT \
public: \
    QT_WARNING_PUSH \
    Q_OBJECT_NO_OVERRIDE_WARNING \
    static const QMetaObject staticMetaObject; \
    virtual const QMetaObject *metaObject() const; \
    virtual void *qt_metacast(const char *); \
    virtual int qt_metacall(QMetaObject::Call, int, void **); \
    QT_TR_FUNCTIONS \
private: \
    Q_OBJECT_NO_ATTRIBUTES_WARNING \
    Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \
    QT_WARNING_POP \
    struct QPrivateSignal {}; \
    QT_ANNOTATE_CLASS(qt_qobject, "")

相当于对于需要Qt信号槽机制的类,比如注册Qt的元对象(即Q_OBJECT),然后Q_OBJECT为这个类添加了一些列元对象结构和相应的函数接口,比如上面的核心staticMetaObjectqt_static_metacall .

查看该头文件编译后的moc_xx.cpp:

  • moc_xx.cpp源码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    
      QT_BEGIN_MOC_NAMESPACE
      QT_WARNING_PUSH
      QT_WARNING_DISABLE_DEPRECATED
      struct qt_meta_stringdata_MyWidget_t {
          QByteArrayData data[4];
          char stringdata0[26];
      };
      #define QT_MOC_LITERAL(idx, ofs, len) \
          Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
          qptrdiff(offsetof(qt_meta_stringdata_MyWidget_t, stringdata0) + ofs \
              - idx * sizeof(QByteArrayData)) \
          )
      static const qt_meta_stringdata_MyWidget_t qt_meta_stringdata_MyWidget = {
          {
      QT_MOC_LITERAL(0, 0, 8), // "MyWidget"
      QT_MOC_LITERAL(1, 9, 8), // "mySignal"
      QT_MOC_LITERAL(2, 18, 0), // ""
      QT_MOC_LITERAL(3, 19, 6) // "mySlot"
        
          },
          "MyWidget\0mySignal\0\0mySlot"
      };
      #undef QT_MOC_LITERAL
        
      static const uint qt_meta_data_MyWidget[] = {
        
       // content:
             8,       // revision
             0,       // classname
             0,    0, // classinfo
             2,   14, // methods
             0,    0, // properties
             0,    0, // enums/sets
             0,    0, // constructors
             0,       // flags
             1,       // signalCount
        
       // signals: name, argc, parameters, tag, flags
             1,    0,   24,    2, 0x06 /* Public */,
        
       // slots: name, argc, parameters, tag, flags
             3,    0,   25,    2, 0x0a /* Public */,
        
       // signals: parameters
          QMetaType::Void,
        
       // slots: parameters
          QMetaType::Void,
        
             0        // eod
      };
        
      void MyWidget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
      {
          if (_c == QMetaObject::InvokeMetaMethod) {
              auto *_t = static_cast<MyWidget *>(_o);
              Q_UNUSED(_t)
              switch (_id) {
              case 0: _t->mySignal(); break;
              case 1: _t->mySlot(); break;
              default: ;
              }
          } else if (_c == QMetaObject::IndexOfMethod) {
              int *result = reinterpret_cast<int *>(_a[0]);
              {
                  using _t = void (MyWidget::*)();
                  if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&MyWidget::mySignal)) {
                      *result = 0;
                      return;
                  }
              }
          }
          Q_UNUSED(_a);
      }
        
      QT_INIT_METAOBJECT const QMetaObject MyWidget::staticMetaObject = { {
          &QWidget::staticMetaObject,
          qt_meta_stringdata_MyWidget.data,
          qt_meta_data_MyWidget,
          qt_static_metacall,
          nullptr,
          nullptr
      } };
        
      const QMetaObject *MyWidget::metaObject() const
      {
          return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
      }
        
      void *MyWidget::qt_metacast(const char *_clname)
      {
          if (!_clname) return nullptr;
          if (!strcmp(_clname, qt_meta_stringdata_MyWidget.stringdata0))
              return static_cast<void*>(this);
          return QWidget::qt_metacast(_clname);
      }
        
      int MyWidget::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
      {
          _id = QWidget::qt_metacall(_c, _id, _a);
          if (_id < 0)
              return _id;
          if (_c == QMetaObject::InvokeMetaMethod) {
              if (_id < 2)
                  qt_static_metacall(this, _c, _id, _a);
              _id -= 2;
          } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
              if (_id < 2)
                  *reinterpret_cast<int*>(_a[0]) = -1;
              _id -= 2;
          }
          return _id;
      }
        
      // SIGNAL 0
      void MyWidget::mySignal()
      {
          QMetaObject::activate(this, &staticMetaObject, 0, nullptr);
      }
      QT_WARNING_POP
      QT_END_MOC_NAMESPACE
    

结合其中Qt生成的注释来理解:Moc编译器创建了一个包含信号和槽信息的元对象。这个元对象是MyWidget类的一部分,并包含了类名、信号和槽的名称、信号和槽的参数类型等信息。

元对象的结构

元对象的定义部分看起来是这样的:

1
2
3
4
5
6
7
8
QT_INIT_METAOBJECT const QMetaObject MyWidget::staticMetaObject = { {
    &QWidget::staticMetaObject, // 父类的元对象
    qt_meta_stringdata_MyWidget.data, // 类名、信号和槽的名称
    qt_meta_data_MyWidget, // 其他元数据,包括信号和槽的参数类型
    qt_static_metacall, // 用于调用信号和槽的函数
    nullptr,// 属性列表
    nullptr// 枚举列表
} };

可以看到,元对象包含了一些元数据,如类名、信号和槽的名称等。这些信息是在编译时由Moc编译器生成的,并存储在qt_meta_stringdata_MyWidgetqt_meta_data_MyWidget这两个静态变量中。

信号和槽的调用

元对象还包含一个qt_static_metacall函数,这个函数是用来调用信号和槽的。这个函数会接收一个指向QObject的指针、一个表示调用类型的枚举值(如InvokeMetaMethod)、一个表示被调用方法的索引以及一个参数列表。

在这个例子中,qt_static_metacall函数的实现是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void MyWidget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod) {
        auto *_t = static_cast<MyWidget *>(_o);
        Q_UNUSED(_t)
        switch (_id) {
        case 0: _t->mySignal(); break; // 如果_id为0,表示调用mySignal信号
        case 1: _t->mySlot(); break; // 如果_id为1,表示调用mySlot槽
        default: ;
        }
    } else if (_c == QMetaObject::IndexOfMethod) {
        // ...
    }
    Q_UNUSED(_a);
}
本文由作者按照 CC BY 4.0 进行授权

读库2305

Qt信号槽机制梳理