C++ Boost.Signals2信号/槽概念

目录
  • 一、关于Boost.Signals2
  • 二、关于Signals库
  • 练习

一、关于Boost.Signals2

Boost.Signals2 实现了信号/槽的概念。一个或多个函数(称为槽)与可以发出信号的对象相关联。每次发出信号时,都会调用链接的函数。

信号/槽概念在开发具有图形用户界面的应用程序时非常有用。可以对按钮进行建模,以便在用户单击它们时发出信号。它们可以支持指向许多函数的链接以处理用户输入。这样就可以灵活地处理事件。

std::function 也可用于事件处理。 std::function 和 Boost.Signals2 之间的一个重要区别是 Boost.Signals2 可以将多个事件处理程序与单个事件相关联。因此,Boost.Signals2更适合支持事件驱动开发,应该是任何需要处理事件的首选。

Boost.Signals2 继承了库 Boost.Signals,后者已被弃用且本书未讨论。

Table of Contents

Signals

Connections

Multithreading

二、关于Signals库

Boost.Signals2 提供类 boost::signals2::signal,可用于创建信号。此类在 boost/signals2/signal.hpp 中定义。或者,您可以使用头文件 boost/signals2.hpp,这是一个主头文件,定义了 Boost.Signals2 中可用的所有类和函数。

Boost.Signals2 定义了 boost::signals2::signal 和其他类,以及命名空间 boost::signals2 中的所有函数。

示例 67.1。 “你好世界!”使用 boost::signals2::signal

#include <boost/signals2.hpp>
#include <iostream>
using namespace boost::signals2;
int main()
{
  signal<void()> s;
  s.connect([]{ std::cout << "Hello, world!\n"; });
  s();
}

boost::signals2::signal 是一个类模板,它期望将用作事件处理程序的函数的签名作为模板参数。在示例 67.1 中,只有签名为 void() 的函数才能与信号相关联。

lambda 函数通过 connect() 与信号 s 相关联。因为 lambda 函数符合所需的签名 void(),所以关联已成功建立。每当触发信号 s 时,都会调用 lambda 函数。

信号是通过像调用常规函数一样调用 s 来触发的。此函数的签名与作为模板参数传递的签名匹配。括号是空的,因为 void() 不需要任何参数。调用 s 会产生一个触发器,而该触发器又会执行之前与 connect() 相关联的 lambda 函数。

示例 67.1 也可以使用 std::function 实现,如示例 67.2 所示。

示例 67.2。 “你好世界!”使用 std::function

#include <functional>
#include <iostream>
int main()
{
  std::function<void()> f;
  f = []{ std::cout << "Hello, world!\n"; };
  f();
}

在示例 67.2 中,调用 f 时也会执行 lambda 函数。虽然 std::function 只能用于类似示例 67.2 的场景,但 Boost.Signals2 提供了更多种类。例如,它可以将多个函数与特定信号相关联(参见示例 67.3)。

示例 67.3。带有 boost::signals2::signal 的多个事件处理程序

#include <boost/signals2.hpp>
#include <iostream>
using namespace boost::signals2;
int main()
{
  signal<void()> s;
  s.connect([]{ std::cout << "Hello"; });
  s.connect([]{ std::cout << ", world!\n"; });
  s();
}

boost::signals2::signal 允许您通过重复调用 connect() 将多个函数分配给特定信号。每当触发信号时,函数都会按照它们与 connect() 关联的顺序执行。

顺序也可以在 connect() 的重载版本的帮助下明确定义,它需要一个 int 类型的值作为附加参数(示例 67.4)。

示例 67.4。具有明确顺序的事件处理程序

#include <boost/signals2.hpp>
#include <iostream>
using namespace boost::signals2;
int main()
{
  signal<void()> s;
  s.connect(1, []{ std::cout << ", world!\n"; });
  s.connect(0, []{ std::cout << "Hello"; });
  s();
}

与前面的示例一样,示例 67.4 显示 Hello, world!。

要从信号中释放关联函数,请调用 disconnect()。

示例 67.5。断开事件处理程序与 boost::signals2::signal 的连接

#include <boost/signals2.hpp>
#include <iostream>
using namespace boost::signals2;
void hello() { std::cout << "Hello"; }
void world() { std::cout << ", world!\n"; }
int main()
{
  signal<void()> s;
  s.connect(hello);
  s.connect(world);
  s.disconnect(world);
  s();
}

Example 67.5

示例 67.5 仅打印 Hello,因为与 world() 的关联在信号被触发之前已释放。

除了 connect() 和 disconnect() 之外,boost::signals2::signal 还提供了几个成员函数(参见示例 67.6)。

示例 67.6。 boost::signals2::signal 的附加成员函数

#include <boost/signals2.hpp>
#include <iostream>
using namespace boost::signals2;
int main()
{
  signal<void()> s;
  s.connect([]{ std::cout << "Hello"; });
  s.connect([]{ std::cout << ", world!"; });
  std::cout << s.num_slots() << '\n';
  if (!s.empty())
    s();
  s.disconnect_all_slots();
}

num_slots() 返回关联函数的数量。如果没有函数关联,num_slots() 返回 0。empty() 告诉您事件处理程序是否已连接。而 disconnect_all_slots() 的作用正如它的名字所说:它释放所有现有的关联。

示例 67.7。处理事件处理程序的返回值

#include <boost/signals2.hpp>
#include <iostream>
using namespace boost::signals2;
int main()
{
  signal<int()> s;
  s.connect([]{ return 1; });
  s.connect([]{ return 2; });
  std::cout << *s() << '\n';
}

在示例 67.7 中,两个 lambda 函数与信号关联。第一个 lambda 函数返回 1,第二个返回 2。

示例 67.7 将 2 写入标准输出。 s 正确接受了两个返回值,但除了最后一个之外的所有返回值都被忽略了。默认情况下,只返回所有关联函数的最后一个返回值。

请注意,s() 不会直接返回上次调用的函数的结果。返回类型为 boost::optional 的对象,取消引用时返回数字 2。触发与任何函数无关的信号不会产生任何返回值。因此,在这种情况下,boost::optional 允许 Boost.Signals2 返回一个空对象。 boost::optional 在第 21 章中介绍。

可以自定义信号,以便相应地处理各个返回值。为此,必须将组合器作为第二个模板参数传递给 boost::signals2::signal。

示例 67.8。使用用户定义的组合器查找最小返回值

#include <boost/signals2.hpp>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace boost::signals2;
template <typename T>
struct min_element
{
  typedef T result_type;
  template <typename InputIterator>
  T operator()(InputIterator first, InputIterator last) const
  {
    std::vector<T> v(first, last);
    return *std::min_element(v.begin(), v.end());
  }
};
int main()
{
  signal<int(), min_element<int>> s;
  s.connect([]{ return 1; });
  s.connect([]{ return 2; });
  std::cout << s() << '\n';
}

组合器是一个具有重载的 operator() 的类。该运算符使用两个迭代器自动调用,这两个迭代器用于访问与特定信号关联的函数。当迭代器被取消引用时,函数被调用并且它们的返回值在组合器中变得可用。然后可以使用标准库中的常用算法,例如 std::min_element() 来计算并返回最小值(参见示例 67.8)。

boost::signals2::signal 使用 boost::signals2::optional_last_value 作为默认组合器。此组合器返回 boost::optional 类型的对象。用户可以定义一个具有任何类型返回值的组合器。例如,示例 67.8 中的组合器 min_element 返回作为模板参数传递给 min_element 的类型。

无法将诸如 std::min_element() 之类的算法作为模板参数直接传递给 boost::signals2::signal。 boost::signals2::signal 期望组合器定义一个名为 result_type 的类型,它表示 operator() 返回值的类型。由于这个类型没有被标准算法定义,所以编译器会报错。

请注意,不可能将迭代器 first 和 last 直接传递给 std::min_element() ,因为该算法需要前向迭代器,而组合器使用输入迭代器。这就是为什么在使用 std::min_element() 确定最小值之前使用向量存储所有返回值的原因。

示例 67.9 修改组合器以将所有返回值存储在容器中,而不是评估它们。它将所有返回值存储在一个向量中,然后由 s() 返回该向量。

示例 67.9。使用用户定义的组合器接收所有返回值

#include <boost/signals2.hpp>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace boost::signals2;
template <typename T>
struct return_all
{
  typedef T result_type;
  template <typename InputIterator>
  T operator()(InputIterator first, InputIterator last) const
  {
    return T(first, last);
  }
};
int main()
{
  signal<int(), return_all<std::vector<int>>> s;
  s.connect([]{ return 1; });
  s.connect([]{ return 2; });
  std::vector<int> v = s();
  std::cout << *std::min_element(v.begin(), v.end()) << '\n';
}

练习

创建一个带有课程按钮的程序。该类应代表图形用户界面中的按钮。添加成员函数 add_handler() 和 remove_handler() ,它们都希望传递一个函数。如果调用另一个名为 click() 的成员函数,则应依次调用已注册的处理程序。通过注册一个将消息写入标准输出的处理程序来实例化按钮并测试该类。调用 click() 来模拟鼠标点击按钮。

到此这篇关于C++ Boost.Signals2信号/槽概念的文章就介绍到这了,更多相关C++ Boost Signals2内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++ Boost ProgramOptions超详细讲解

    目录 一.说明 二.示例Boost.ProgramOptions 一.说明 Boost.ProgramOptions Boost.ProgramOptions 是一个可以轻松解析命令行选项的库,例如,控制台应用程序.如果您使用图形用户界面开发应用程序,命令行选项通常并不重要. 要使用 Boost.ProgramOptions 解析命令行选项,需要以下三个步骤: 定义命令行选项.您给它们命名并指定哪些可以设置为一个值.如果命令行选项被解析为键/值对,您还可以设置值的类型——例如,它是字符串还是数字

  • C++ Boost Flyweight库使用介绍

    目录 一.说明 二.库Boost.Flyweight 炼习 一.说明 以下库用于设计模式. Boost.Flyweight 有助于在程序中使用许多相同的对象并且需要减少内存消耗的情况. Boost.Signals2 使得使用观察者设计模式变得容易.这个库被称为 Boost.Signals2 因为它实现了信号/槽的概念. Boost.MetaStateMachine 使得将状态机从 UML 转移到 C++ 成为可能. 本节内容 66. Boost.Flyweight 67. Boost.Signa

  • C++ Boost Archive超详细讲解

    目录 一.说明 二.关于Archive库 一.说明 对Boost.Serialization库的应用,存在如下内容: Archive Pointers and References Serialization of Class Hierarchy Objects Wrapper Functions for Optimization Boost.Serialization 库可以将 C++ 程序中的对象转换为可以保存和加载以恢复对象的字节序列.有不同的数据格式可用于定义生成字节序列的规则. Boo

  • C++ Boost MetaStateMachine定义状态机超详细讲解

    目录 一.说明 二.示例和代码 一.说明 Boost.MetaStateMachine 用于定义状态机.状态机通过对象的状态来描述对象.它们描述了存在哪些状态以及状态之间可能存在哪些转换. Boost.MetaStateMachine 提供了三种不同的方式来定义状态机.创建状态机所需编写的代码取决于前端. 如果使用基本前端或函数前端,则可以用常规方式定义状态机:创建类,从 Boost.MetaStateMachine 提供的其他类派生它们,定义所需的成员变量,并编写所需的 C++自己编码.基本前

  • C++ Boost Assign超详细讲解

    目录 说明 Exercise 说明 Boost.Assign Boost.Assign 库提供了帮助函数来初始化容器或向容器添加元素.如果需要将许多元素存储在一个容器中,这些函数尤其有用.多亏了 Boost.Assign 提供的函数,您不需要重复调​​用像 push_back() 这样的成员函数来将元素一个一个地插入到容器中. 如果您使用支持 C++11 的开发环境,则可以从初始化列表中获益.通常您可以将任意数量的值传递给构造函数来初始化容器.多亏了初始化列表,你不必依赖 C++11 的 Boo

  • C++ Boost Utility超详细讲解

    目录 一.说明 二.Boost.Utility库示例和代码 一.说明 Boost.Utility 库是杂项.有用的类和函数的集合,它们太小而无法在独立库中维护.虽然实用程序很小并且可以快速学习,但它们完全无关.与其他章节中的示例不同,此处的代码示例不是相互构建的,因为它们是独立的实用程序. 虽然大多数实用程序都在 boost/utility.hpp 中定义,但有些实用程序有自己的头文件.以下示例包括所介绍的实用程序的相应头文件. 二.Boost.Utility库示例和代码 示例 69.1.使用

  • C++ Boost Uuid超详细讲解

    目录 一.说明 二.Boost.Uuid库示例和代码 一.说明 Boost.Uuid 为 UUID 提供生成器. UUID 是不依赖于中央协调实例的通用唯一标识符.例如,没有数据库存储所有生成的 UUID,可以检查这些 UUID 是否使用了新的 UUID. UUID 由必须唯一标识组件的分布式系统使用.例如,Microsoft 使用 UUID 来识别 COM 世界中的接口.对于为 COM 开发的新接口,可以轻松分配唯一标识符. UUID 是 128 位数字.存在多种生成 UUID 的方法.例如,

  • c++ Qt信号槽原理

    1.说明 使用Qt已经好几年了,一直以为自己懂Qt,熟悉Qt,使用起来很是熟练,无论什么项目,都喜欢用Qt编写.但真正去看Qt的源码,去理解Qt的思想也就近两年的事. 本次就着重介绍一下Qt的核心功能--信号槽机制,相信接触过Qt的人都能很熟悉地使用,甚至,大部分人还能轻松地说出信息槽的几种用法.但是信号槽的核心可不是简单说说就能说清楚的. 那么,本次,就从Qt的源码中讲解一下信号槽的机制. 其实,直到写这篇文章,我也没有完全看明白相关的源码,只是明白了其中的大部分以及使用机制,其中还有很多细节

  • 深入理解Qt信号槽机制

    目录 1. 信号和槽概述 1.1 信号的本质 1.2 槽的本质 1.3 信号和槽的关系 1. 信号和槽概述 信号槽是 Qt 框架引以为豪的机制之一.所谓信号槽,实际就是观察者模式(发布-订阅模式).当某个`事件`发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal).这种发出是没有目的的,类似广播.如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,将想要处理的信号和自己的一个函数(称为槽(slot))绑定来处理这个信号.也就是说,当信号发出时,被连

  • 详解c++实现信号槽

    目录 用c++实现信号槽机制(signal-slot) 总结 用c++实现信号槽机制(signal-slot) 信号槽机制的个人理解:信号槽是在两个c++类对象之间建立联系的通道,其中一个对象可称之为信号发送者(sender),另一个对象可称之为信号接收者(recver),sender通过信号槽发出信号后,recver就可以执行函数进行某些操作.也就是说应用程序通过信号槽可以在两个互不相关的对象之间建立起逻辑关系,使程序开发变得简洁.方便. 信号槽本质是由c++定义的类组成,分为两个部分:槽类和

  • Qt开发实现跨窗口信号槽通信

    多窗口通信,如果是窗口类对象之间互相包含,则可以直接开放public接口调用,不过,很多情况下主窗口和子窗口之间要做到异步消息通信,就必须依赖到跨窗口的信号槽,以下是一个简单的示例. 母窗口 mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QLabel> #include <QString> class MainWindow : public

  • Python深度学习实战PyQt5信号与槽的连接

    目录 1. 信号与槽(Signals and slots) 1.1 信号与槽的原理 1.2 信号发送者与槽的接收者 2. QtDesigner 建立信号与槽的连接 2.1 信号与槽的连接:不同的发送者与接收者,槽函数为控件的内置函数 QtDesigner 设置信号/槽的连接的操作步骤如下: 2.2 信号与槽的连接:不同的发送者与接收者,槽函数为自定义函数 QtDesigner 设置信号/槽的连接的操作步骤如下: 2.3 信号与槽的连接:相同的发送者与接收者,槽函数为控件的内置函数 2.4 信号与

  • PyQt5通信机制 信号与槽详解

     前言 信号和槽是PyQt编程对象之间进行通信的机制.每个继承自QWideget的控件都支持信号与槽机制.信号发射时(发送请求),连接的槽函数就会自动执行(针对请求进行处理).本文主要讲述信号和槽最基本.最经常使用方法.就是内置信号和槽的使用的使用方法. 内置信号和槽 所谓内置信号与槽的使用.是指在发射信号时,使用窗口控件的函数,而不是自定义的函数.信号与槽的连接方法是通过QObject.signal.connect将一个QObject的信号连接到另一个QObject的槽函数. 在任何GUI设计

  • 浅谈Qt信号与槽的各种连接方式

    目录 简介 连接信号槽 connect 函数的第五个参数 信号与槽的连接方式 简介 信号槽是 Qt 框架引以为豪的机制之一.当用户触发某个事件时,就会发出一个信号(signal),这种发出是没有目的的,类似广播.如果有对象对这个信号感兴趣,它就会连接(connect)绑定一个函数(称为槽slot)来处理这个信号.也就是说当信号发出时,被连接的槽函数会自动被回调.这有点类似与开发模式中的观察者模式,即当发生了感兴趣的事件,某一个操作就会被自动触发 信号和槽是Qt特有的信息传输机制,是Qt设计程序的

  • 初步理解Python进程的信号通讯

    信号的概念 信号(signal)--     进程之间通讯的方式,是一种软件中断.一个进程一旦接收到信号就会打断原来的程序执行流程来处理信号. 几个常用信号: SIGINT     终止进程  中断进程  (control+c) SIGTERM   终止进程     软件终止信号 SIGKILL   终止进程     杀死进程 SIGALRM 闹钟信号 进程结束信号 SIGTERM和SIGKILL的区别 SIGTERM比较友好,进程能捕捉这个信号,根据您的需要来关闭程序.在关闭程序之前,您可以结

  • PyQt5每天必学之事件与信号

    这一部分我们将探索 PyQt5 的事件和信号是如何在应用程序中实现的. Events事件 所有的GUI应用程序都是事件驱动的.应用程序事件主要产生自用户,但它们也可通过其他方法来产生,例如一个互联网连接,一个窗口管理器,或计时器.当我们调用应用程序的exec_()方法,应用程序进入主循环.主循环检测各种事件,并把它们发送到事件对象. 在事件模型中,有三个参与者: event source(事件源) event object(事件对象) event target(事件目标) 事件源是对象的状态改变

随机推荐