关于信号和槽,Q_OBJECT 宏在类的声明中添加了一个虚函数qt_metacall() 声明,稍后将由moc 定义。 (它还添加了一些转换声明,但这在这里并不重要。)
moc 然后读取头文件,当它看到宏时,它会生成另一个名为 moc_headerfilename.cpp 的 .cpp 文件,其中包含对虚函数的定义 - 您可能会问自己为什么可以逃脱在没有正确定义信号的情况下在头文件中提及signals:。
因此,当调用信号时,将执行 mocfile 中的定义,并使用信号名称和信号参数调用 QMetaObject::activate()。
然后,activate() 函数会确定已建立哪些连接并获取相应插槽的名称。
然后它调用qt_metacall 并使用槽名称和为信号提供的参数,元调用函数在一个大的switch—case 语句的帮助下将其委托给真正的槽。
由于在 C++ 中没有关于信号和槽的实际名称的真正运行时信息,正如已经注意到的,这些将由 SIGNAL 和 SLOT 宏编码为简单的 const char*s (在名称中添加“1”或“2”以区分信号和插槽)。
如qobjectdefs.h中所定义:
#define SLOT(a) "1"#a
#define SIGNAL(a) "2"#a
—
Q_OBJECT 宏所做的另一件事是在您的对象中定义 tr() 函数,这些函数可用于翻译您的应用程序。
编辑
正如你问qt_metacast 在做什么。它检查一个对象是否属于某个类,如果它确实返回指向它的指针。如果不是,则返回 0。
Widget* w = new Widget();
Q_ASSERT(w->qt_metacast("Widget") != 0);
Q_ASSERT(w->qt_metacast("QWidget") != 0);
Q_ASSERT(w->qt_metacast("QObject") != 0);
Q_ASSERT(w->qt_metacast("UnrelatedClass") == 0);
这是提供一些运行时反射所必需的,否则这是不可能的。例如,该函数在QObject::inherits(const char *) 中调用,并简单地检查继承。