新手到C ++这里。我正在阅读A Deeper Look at Signals and Slots,它声称1)回调本质上是类型不安全的,2)为了使它们安全,你需要在你的函数周围定义一个纯虚拟类包装器。我很难理解为什么这是真的。例如,以下是Qt在tutorial page for signals and slots上提供的代码:
// Header file
#include <QObject>
class Counter : public QObject
{
Q_OBJECT
public:
Counter() { m_value = 0; }
int value() const { return m_value; }
public slots:
void setValue(int value);
signals:
void valueChanged(int newValue);
private:
int m_value;
};
// .cpp file
void Counter::setValue(int value)
{
if (value != m_value) {
m_value = value;
emit valueChanged(value);
}
}
// Later on...
Counter a, b;
QObject::connect(&a, SIGNAL(valueChanged(int)),
&b, SLOT(setValue(int)));
a.setValue(12); // a.value() == 12, b.value() == 12
b.setValue(48); // a.value() == 12, b.value() == 48
以下是使用回调重写的代码:
#include <functional>
#include <vector>
class Counter
{
public:
Counter() { m_value = 0; }
int value() const { return m_value; }
std::vector<std::function<void(int)>> valueChanged;
void setValue(int value);
private:
int m_value;
};
void Counter::setValue(int value)
{
if (value != m_value) {
m_value = value;
for (auto func : valueChanged) {
func(value);
}
}
}
// Later on...
Counter a, b;
auto lambda = [&](int value) { b.setValue(value); };
a.valueChanged.push_back(lambda);
a.setValue(12);
b.setValue(48);
如您所见,回调版本是类型安全的,并且比Qt版本despite them claiming that it's not更短。除了Counter
之外,它没有定义任何新类。它仅使用标准库代码,不需要特殊的编译器(moc)即可工作。那么,为什么信号和插槽比回调更受欢迎? C ++ 11是否简单地废弃了这些概念?
感谢。
答案 0 :(得分:9)
两者之间存在巨大差异:线程。
传统的回调是在调用线程的上下文中始终调用。信号和插槽不是这样 - 只要线程正在运行一个事件循环(如果它是一个QThread
),那么插槽就可以在任何线程中。
当然,您可以通过回调手动完成所有这些操作 - 多年来我使用Windows风格的消息泵编写了许多Win32应用程序,这些消息泵可以跨线程处理回调 - 但它很多样板代码,编写,维护或调试没什么好处。
答案 1 :(得分:3)
为什么信号和插槽比普通旧回调更好?
因为信号很像旧的回调,除了具有额外的功能和与Qt API深度集成之外。它不是火箭科学 - 回调+额外功能+深度整合比单独回调更大。 C ++可能最终提供了一种更简洁的回调方式,但这并不能取代Qt信号和插槽,更不用说它们已经过时了。
从Qt 5开始,插槽方面的相关性稍差,这使得信号可以连接到任何功能。但是,插槽仍然与Qt元系统集成,许多Qt API使用它来使事情有效。
是的,您可以使用回调来处理几乎所有信号应该实现的内容。但它并不容易,它更加冗长,它不能自动处理排队连接,它不会像信号那样与Qt集成,你也可以解决这个问题,但它会变得更加冗长。
在QML的情况下,现在Qt是Qt的主要焦点,你基本上坚持使用Qt的信号。所以我认为信号就在这里。
信号和插槽更好&#34;因为Qt是在概念上围绕它们构建的,所以它们是API的一部分,并被许多API使用。这些概念已经在Qt中存在了很长时间,从C ++开始提供很多回调支持的日子开始,除了从C继承的普通旧函数指针之外,这也是Qt不能简单地切换到std回调的原因 - 它会打破很多东西,是一种不必要的努力。同样的原因Qt继续使用那些邪恶的不安全的普通旧指针而不是智能指针。信号和插槽作为一个概念并不是过时的,在使用Qt时技术上更是如此。 C ++在游戏中简直太晚了。期望每个人现在都急于从他们自己的巨大代码库中实现,这是不现实的,因为C ++最终提供了替代方案作为语言标准库的一部分。
答案 2 :(得分:0)
通常:Signal和Slot与回调不同,因为它从处理程序(Slot)中将呼叫(信号)与信号(信号)分离。 这意味着:您可以将插槽注册到其他线程上,可以从多个插槽中监听一个信号,轻松更改排队策略。 但是它有它的价格(至少在QT世界中是这样的...):字符串评估,更多的内部控制流程等。 简而言之,它是更高层次的概念。
话虽如此,您可以通过简单的回调来完成所有操作,但这将彻底改变方向。