Qt如何实现信号和槽?

20

有人能向我解释一下Qt信号槽机制实现的基本思想吗? 我想知道所有这些Q_OBJECT宏在“普通C ++”中都做了什么。 这个问题不是关于信号槽用法的。

补充: 我知道Qt使用moc编译器将Qt-C++转换为普通C ++。 但是moc做了什么呢? 我尝试阅读“moc_filename.cpp”文件,但我不知道这意味着什么。

void *Widget::qt_metacast(const char *_clname)
{
if (!_clname) return 0;
if (!strcmp(_clname, qt_meta_stringdata_Widget))
    return static_cast<void*>(const_cast< Widget*>(this));
return QDialog::qt_metacast(_clname);
}

dup: https://dev59.com/6XM_5IYBdhLWcg3wXx5N - elcuco
3个回答

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


1
还可以查看这篇博客文章,以了解更多细节:http://woboq.com/blog/how-qt-signals-slots-work.html - guruz

3

在“纯C ++”中,这些宏完全没有作用-它们会扩展为空字符串(我想)。

QT使用元对象编译器为启用Q_OBJECT的类(实现您定义的信号/插槽等内容)生成C ++代码。

您可以在官方文档中了解更多信息。


确实。事实上,如果您编译代码,可以查看生成的源代码。 - San Jacinto
1
当信号和槽被扩展时,需要有一个小的区别,因为您可以将信号连接到另一个信号,这样就不清楚字符串代表什么了。 - Debilski

-2
基本思想是,您可以连接您的对象,使它们在信号完成时执行一个方法(插槽)。
connect(pistol,SIGNAL(sigShoot()),runner,SLOT(slotRun()))

通过上述连接,当枪发出信号时,赛跑者将执行其插槽。
为此,您必须在各自的类中声明您的信号和插槽。
这是基本想法。
祝你好运!

5
但是原帖的问题是关于内部实现的,而不是如何使用它。 - Nicolás
这个想法看起来像是Dojo处理消息系统的方式。 - xiao 啸

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接