什么是信号和插槽?

时间:2008-11-23 20:12:04

标签: c++ design-patterns signals-slots

有人可以用简单的术语解释“信号和插槽”模式吗?

6 个答案:

答案 0 :(得分:23)

信号和插槽是一种解耦发送方(信号)和零个或多个接收方(插槽)的方法。假设您有一个系统,其中包含您希望对系统中对这些事件感兴趣的任何其他部分可用的事件。您可以使用信号和插槽模式,而不是将生成事件的代码硬连接到想要了解这些事件的代码。

当发送者发出事件信号时(通常通过调用与该事件/信号相关联的功能),将自动调用该事件的所有接收者。这允许您在程序的生命周期内根据需要连接和断开接收器。

由于这个问题被标记为C ++,因此这里有一个Boost.Signals库的链接,它有更全面的解释。

答案 1 :(得分:13)

我认为,当您将信号和广告位视为Observer Pattern or Publish/Subscriber Pattern的可能实施工具时,可以最好地描述信号和广告位。发布方有一个signal,例如buttonPressed(IdType)。只要按下按钮,就会调用连接到该信号的所有插槽。插槽位于订户端。例如,插槽可以是sendMail(IdType)

随着事件“按下按钮”,插槽将知道按下了哪个按钮,因为id已被移交。 IdType表示通过发布服务器和订阅服务器之间的连接发送的数据类型。订阅者可能的操作是connect(signal, slot),可以将buttonPressed(IdType)sendMail(IdType)连接起来,这样如果按下该按钮,则会调用该特定广告位。

关于这一点的好处是订户(插槽侧)不需要关心信号的细节。它只需要连接。因此,这里我们有很多松散耦合。您可以更改按钮实现,但插槽的接口仍然是相同的。

查看Qt Signals/SlotsBoost Signals了解更多信息。

答案 2 :(得分:6)

想象一下,在您的应用程序中使用GUI。大多数情况下,控制流程不是非常线性的,即不是有一个明确的动作序列,而是让用户与GUI交互(如按钮,菜单等)。

这本质上是一个事件驱动模型,可以很好地实现信号和插槽模式。信号是由对象生成的事件(想想GUI组件),而槽是这些事件的接收者。

以下是一个示例:假设您有一个复选框,表示为编程语言中的对象。该复选框可能会出现多种情况:它可以切换,这反过来也意味着它可以设置也可以不设置。那些是它可以发出的信号。我们将它们命名为checkboxToggled,checkboxSet和checkboxUnset。如您所见,在此示例中,复选框将在切换时始终发出checkboxToggled信号,但也恰好是其他两个信号中的一个,具体取决于状态的变化。

现在假设有一些其他对象,即一个标签,为了这个例子,它总是作为一个对象存在,但可以“出现”和“消失”,系统发出蜂鸣声(也由一个对象表示),这可以简单嘟。那些是对象所拥有的插槽。我们称之为“messageAppear”,“messageDisappear”和“beep”。

假设每次切换复选框时都希望系统发出蜂鸣声,并且标签会根据用户是选中还是清除复选框而显示或消失。

您可以将以下信号连接到以下插槽(左侧信号,右侧插槽):

checkboxToggled -> beep
checkboxSet -> messageAppear
checkboxUnset -> messageDisappear

基本上就是这样。

信号和插槽也可能有参数。例如,使用设置数值的滑块,您希望在用户移动滑块后立即发送更改的值以及发出的信号:sliderChanged(int)。

当然,要实际做一些有用的事情,你会写一些自己的类,其中包含一些自己的信号和插槽。这很容易完成并使用这些自己的信号和插槽,您可以通过事件驱动的方式与GUI或代码的其他部分进行交互。

请记住,信号和插槽通常是对称的,因为通常可能存在与插槽相对应的信号。例如,复选框可能会在切换时发出信号,但它也可能包含一个切换复选框本身的插槽。可以很容易地实现单独的复选框,这些复选框总是彼此相反地设置。

答案 3 :(得分:4)

我假设你在谈论QT的信号和插槽 这很简单。

类的实例可以触发信号,而另一个类的另一个实例可以在插槽中捕获该信号。它有点像函数调用,只是调用函数的人不需要知道谁想要接收调用。

说明的最佳方式是举例 类QPushButton有一个信号QPushButton :: clicked()。单击按钮时会触发该信号。按钮不需要知道谁有兴趣知道发生了点击。它只是发出信号,任何有兴趣的人都可以连接它 放置按钮的QDialog非常有兴趣知道何时单击按钮。它有插槽MyDialog :: buttonClicked()。在MyDialog c'tor上你需要连接()按钮click()信号到对话框的buttonClicked()插槽,以便在信号被触发时调用插槽。

一堆更高级的东西:

  • 参数,信号可以有参数,这些参数也可以选择性地传递给插槽。
  • 跨线程调用 - 如果您正在进行需要跨线程的信号槽连接,那么QT将自动缓冲信号并将它们排队到正确的线程。这种情况会自动发生,例如当GUI线程需要与工作线程通信时。

Here's more information in QT's documentation.

答案 4 :(得分:1)

我发现信号和广告位的最佳示例和解释是this code project article

答案 5 :(得分:0)

有一种常见的误解,即课程是人,狗,自行车等名词。然后认为一个人(实例)有一只狗和一辆自行车是有道理的。

让我们从什么对象(应该是)开始。对象是数据和过程。什么是节目?数据和程序。对象应该是(相对)“小”独立子程序。因为oo编程教学非常模糊和误用(需要引用),人们认为一切都需要成为一个类或一个对象。事实并非如此,对象是具有“小”API(公共子例程)的“小”独立程序。 有些程序员甚至不会将他们的项目分解为子程序,只使用数据和程序更合适的对象。

现在,假设我们同意对象是程序,我们可能同意在大多数情况下,程序不需要具有相似大小和复杂度的其他程序的副本(即,对象不是指向另一个对象的指针),它可能需要较小的程序来管理数据(如数据结构),但imho不需要另一个对象。

为什么呢?因为耦合对象使它们依赖。为什么那么糟糕?因为当对象是独立的时,您可以测试它们,并且还向其他程序员和客户承诺该对象(一个小的独立程序)能够高度确定地执行某些任务。只要没有对该对象进行任何更改,您也可以确保它继续执行。

那么什么是插槽和信号?如果您了解对象就像程序一样,理想情况下它们不应该保存副本或指向其他对象的指针,而不是您需要某种方式进行通信。例如,在您的计算机上运行的进程可以使用套接字,IP地址和端口进行通信。对象可以使用与RPC被称为信号和插槽非常相似的东西。这些数据结构旨在作为存储对象子例程(slots)的两个较大对象之间的中介,并允许其他对象使用合适的参数调用(signal)这些子例程(slots)除了他们需要的参数之外,对这些其他对象一无所知。

因此,底层结构是(可能)强类型过程指针的集合(可能是数组),其他对象可以使用合适的参数调用,而无需指向这些对象的指针。调用者只需要访问信号对象(不包含任何实现细节)来定义预期的参数。

这也很灵活,因为它允许一些特殊用例,例如仅响应一次信号的插槽,一个信号的多个插槽以及其他类似的用例,如去抖动。