关于信号和槽,
Q_OBJECT
宏将在类声明中添加一个虚函数
qt_metacall()
的声明,该函数稍后将由
moc
进行定义。(它还添加了一些转换的声明,但这里不太重要。)
然后,
moc
会读取头文件,当它看到该宏时,它会生成另一个名为
moc_headerfilename.cpp
的
.cpp
文件,其中包含虚函数的定义,以及您可以在头文件中提到而无需适当定义的信号。
所以,当调用信号时,从 mocfile 的定义执行,并调用
QMetaObject::activate()
,并传入信号的名称和参数。
activate()
函数然后找出已建立的连接,并检索适当 slot 的名称。
然后再使用与信号和槽相关联的参数来调用
qt_metacall
并将其委托给真正的 slots,而这个元调用函数则通过一个大的
switch
-
case
语句来实现。
由于在 C++ 中不存在关于信号和槽实际名称的运行时信息,因此这些名称将由
SIGNAL
和
SLOT
宏编码为简单的
const char*
(名称中可能会添加 "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 *)
中调用该函数并简单地检查继承关系。