Qt 如何在引擎盖下实现信号和插槽?

Qt 如何在引擎盖下实现信号和插槽?,qt,qt4,signals-slots,Qt,Qt4,Signals Slots,这个问题已经在这个论坛上被问到了,但我不理解这个概念 我在四处阅读,似乎信号和插槽是使用函数指针实现的,即信号是一个大函数,它内部调用所有连接的插槽(函数指针)。这是正确的吗?生成的moc文件在整个故事中扮演什么角色?我不明白信号函数如何知道要调用哪些插槽,即哪些插槽连接到此信号 感谢您的时间Qt以类似于解释语言的方式实现这些东西。即,它构造符号表,将信号名称映射到函数指针,维护它们,并在需要时按函数名称查找函数指针 每次发出信号时,即写入 emit something(); 您实际上调用了s

这个问题已经在这个论坛上被问到了,但我不理解这个概念

我在四处阅读,似乎信号和插槽是使用函数指针实现的,即信号是一个大函数,它内部调用所有连接的插槽(函数指针)。这是正确的吗?生成的moc文件在整个故事中扮演什么角色?我不明白信号函数如何知道要调用哪些插槽,即哪些插槽连接到此信号


感谢您的时间

Qt以类似于解释语言的方式实现这些东西。即,它构造符号表,将信号名称映射到函数指针,维护它们,并在需要时按函数名称查找函数指针

每次发出信号时,即写入

emit something();
您实际上调用了
something()
函数,该函数由元对象编译器自动生成并放入
*.moc
文件中。在该函数中,检查该信号当前连接到的插槽,并通过符号表(以上述方式)顺序调用相应的插槽函数(在您自己的源代码中实现)。和<代码> EIT >像其他QT特定关键字一样,在C++ >预处理程序后被丢弃。实际上,在其中一个Qt头()中,存在这样的行:

#define slots 
#define signals protected
#define emit
连接函数(
connect
)只修改
*.moc
文件中维护的符号表,传递给它的参数(使用
SIGNAL()
和`插槽宏)也会进行预处理以匹配表


这是总的想法。在他或她的另一个答案中,为我们提供了有关此主题的链接和链接。

我想我应该添加以下内容

有,有,这可以看作是对它的一个非常详细的扩展,使用改进的(尽管仍然不完美)代码语法高亮显示

这是我的简短复述,可能会出错)

基本上,当我们在类定义中插入
Q_对象
宏时,预处理器将其扩展为静态
QMetaObject
实例声明,该声明将由同一类的所有实例共享:

class ClassName : public QObject // our class definition
{
    static const QMetaObject staticMetaObject; // <--= Q_OBJECT results to this

    // ... signal and slots definitions, other stuff ...

}
现在,当
moc
为Qt类头
headername.h
创建
moc\u headername.cpp
文件时,它会将签名字符串和正确初始化
d
结构所需的其他数据放在那里,然后使用此数据为
staticMetaObject
singleton编写初始化代码

它所做的另一件重要事情是为对象的
qt_metacall()
方法生成代码,该方法接受对象的方法id和参数指针数组,并通过长
开关调用该方法,如下所示:

int ClassName::qt_metacall(..., int _id, void **_args)
{
    // ... skip ...
    switch (_id) {
        case 0: signalOrSlotMethod1(_args[1], _args[2]); break; // for a method with two args
        case 1: signalOrSlotMethod2(_args[1]); break; // for a method with a single argument
        // ... etc ...
    }
    // ... skip ...
}
最后,对于每个信号,
moc
生成一个实现,其中包含一个
QMetaObject::activate()
调用:

void ClassName::signalName(argtype1 arg1, argtype2 arg2, /* ... */)
{
    void *_args[] = { 0, // this entry stands for the return value
                      &arg1, // actually, there's a (void*) type conversion
                      &arg2, // in the C++ style
                      // ...
                    };
    QMetaObject::activate( this, 
                           &staticMetaObject, 
                           0, /* this is the signal index in the qt_metacall() map, I suppose */ 
                           _args
                         );
}
最后,
connect()
调用将字符串方法签名转换为它们的整数ID(由
qt_metacall()
使用的ID),并维护信号到插槽连接的列表;当信号发出时,
activate()

总之,静态
QMetaObject
实例存储“元信息”(方法签名字符串等),生成的
qt_metacall()
方法提供“方法表”,允许任何信号/插槽由索引调用,由
moc
生成的信号实现通过
activate()
使用这些索引,最后,
connect()
负责维护信号到插槽索引映射的列表

*注意:当我们想要在不同的线程之间传递信号时,这个方案有一个复杂的情况(我怀疑必须查看
blocking\u activate()
code),但我希望总体思路保持不变)

这是我对链接文章的粗略理解,很容易出错,因此我建议直接阅读)

另外,我想提高我对Qt实现的理解——请告诉我复述中的任何不一致之处


由于我的另一个(早些时候)答案被一些热心的编辑删除了,我将在这里添加文本(我遗漏了一些没有包含在Pavel Shved的帖子中的细节,我怀疑删除答案的人是否在意。)

@帕维尔·施维德:

我很确定Qt头中的某个地方有一行:

#定义发射

只是确认一下:谷歌代码搜索在旧的Qt代码中找到了它。很可能它还在那里);找到的位置路径为:

› slackware-7.1› 贡献› kde-1.90› qt-2.1.1.tgz› usr› lib› qt-2.1.1› src› 内核› qobjectdefs.h


另一个补充链接:参见Andreas Pakulat的答案



下面是另一个答案:

基本正确,您可以在发出时设置一个断点,然后逐步完成信号传递过程。虽然通常直接发送,但信号可能会排队,当您想在不同的线程中连接两个对象时,这是必要的。另见:
void ClassName::signalName(argtype1 arg1, argtype2 arg2, /* ... */)
{
    void *_args[] = { 0, // this entry stands for the return value
                      &arg1, // actually, there's a (void*) type conversion
                      &arg2, // in the C++ style
                      // ...
                    };
    QMetaObject::activate( this, 
                           &staticMetaObject, 
                           0, /* this is the signal index in the qt_metacall() map, I suppose */ 
                           _args
                         );
}