signals2 基于Boost里的另一个库signals,实现了线程安全的观察者模式。它是一种函数回调机制,当一个信号关联了多个槽时,信号发出,这些槽将会被调用。google的base库里用的多的模式是:observer,delegate,callback。用来做相互调用的解耦,但是感觉也没有信号槽用的方便。

其实Qt也提供了它自己的信号和槽机制,那个是非常的灵活和好用的,但是它依赖于Qt的框架,所以退而求其次,选择了Boost提供了signals2;

signals2库位于命名空间boost::signals2中,为了使用它,需要包含头文件<boost/signals2.hpp>;

文章目录

信号(Signal)

连接(connect)

实例

不带返回值的槽函数

合并器

断开连接

临时连接

阻塞连接

触发成员中的槽函数

自动断开


信号(Signal)

signal是不可拷贝的,如果将signal作为类的成员变量,那么类将不能被拷贝,除非使用只能智能或者是引用间接的持有它;

signal是一个模板类,它的定义如下:

    template<typename Signature, typename Combiner = boost::signals2::optional_last_value<R>, typename Group = int, typename GroupCompare = std::less<Group>, typename SlotFunction = boost::function<Signature>, typename ExtendedSlotFunction = boost::function<R (const connection &, T1, T2, ..., TN)>, typename Mutex = boost::signals2::mutex> class signal;

第一个模板参数Signature的含义和function相同,也是一个函数类型,表示signal调用的函数(槽,事件处理handler),例如:

signal<void(int, double)> sig;

第二个模板参数Combiner是一个函数对象,它被称为‘合并器’,用于组合所有槽的返回值,默认是boost::signals2::optional_last_value<R>,返回最后一个被调用的槽的返回值;

第三个模板参数Group是槽编组的类型,你可以为你的槽设置不同的组,默认组的类型是int,通常情况下,不需要更改;

连接(connect)

connection connect(const group_type &group,const slot_type &slot, connect_position position = at_back)

它作为signal的成员函数,具有三个参数,第一个参数表示这个槽所属的组,第二的参数表示信号触发哪个槽函数,而最后的参数,表示槽函数在响应队列中响应的位置,默认at_back表示这个槽函数出来队列的末尾,它将在其他槽函数之后被调用。

实例

不带返回值的槽函数

#include <iostream>
#include <boost/signals2.hpp>
using namespace boost::signals2;
void slots1() {std::cout << "slot 1 called" << std::endl;
}void slots2(int a) {std::cout << "slot 2 called " << a << std::endl;
}void slots3(int a) {std::cout << "slot 3 called " << a << std::endl;
}void slots4(int a) {std::cout << "slot 4 called " << a << std::endl;
}int main() {signal<void()>sig1;sig1.connect(&slots1);sig1(); // the slot 1 calledsignal<void(int)>sig2;sig2.connect(1, &slots2);sig2.connect(2, &slots3);sig2.connect(2, &slots4, at_front); // slot 4 处于 第二组的最前面// 槽函数的调用,首先是比较连接的组的先后循序,然后根据组内循序调用;sig2(2); //  slot 2 called slot 4 called slots3 calledreturn 0;
}

当槽函数带参数的时候,参数是通过信号传递的,所以需要保持信号和槽的参数的个数一致

结果如下:

带参数的槽函数

#include <iostream>
#include <boost/signals2.hpp>
using namespace boost::signals2;
int slots1(int a) {std::cout << "slot 1 called " << a << std::endl;return a + 1;
}int slots2(int a) {std::cout << "slot 2 called " << a << std::endl;return a + 2;
}int slots3(int a) {std::cout << "slot 3 called " << a << std::endl;return a + 3;
}int main() {signal<int(int)> sig;sig.connect(&slots1);sig.connect(&slots2, at_front);sig.connect(&slots3);std::cout << *sig(0) << std::endl;return 0;
}

在默认情况下,一个信号连接多个槽函数,并且槽函数是带有返回值的,那么这个信号将返回槽函数队列中的最后一个的返回值。

结果如下:

合并器

自定义合并器可以让我们处理多个槽的返回值;

template<typename T>
struct Combiner {typedef vector<T> result_type;template<typename InputIterator>result_type operator()(InputIterator first, InputIterator last) const {if(first == last) {return result_type(0);}return result_type(first, last);}
};

这是一个典型的合并器,它返回一个拥有所有槽的返回值的一个vector,我们可以随便定义合并器的返回类型,但要注意,一定要通过 typedef your_type result_type去注册一下你的返回值类型;

具体的用法如下:

#include "boost/signals2.hpp"
#include <iostream>
#include <vector>
using namespace std;
using namespace boost::signals2;template<typename T>
struct Combiner {typedef vector<T> result_type;template<typename InputIterator>result_type operator()(InputIterator first, InputIterator last) const {if(first == last) {return result_type(0);}return result_type(first, last);}
};int slots3(int x) {return x + 3;
}int slots4(int x) {return x + 4;
}int main() {signal<int(int), Combiner<int> > sig;sig.connect(&slots3);sig.connect(&slots4);auto result = sig(1);for(const auto& i : result) {cout << i << endl;}    return 0;
}

断开连接


// 以上省略一些代码
sig.connect(0, &slots1);
sig.connect(0, &slots2);
connection c1 = sig.connect(1, &slots3);
sig.connect(2, &slots4);
sig.connect(2, &slots5);
sig();
sig.disconnect(0); // 断开组号为0的连接
cout << sig.num_slots() << endl; // 还有三个连接
sig();
sig.disconnect(2); // 断开组号为2的连接
sig();
c1.disconnect(); // 断开slot3的连接

以上两种方法都是可以的;

临时连接

Boost提供了一个临时的连接方式scoped_connection,也就是有作用域的连接;

// 以上省略了一些代码
sig.connect(&slots1);
{ // 进入作用域, 建立临时的连接scoped_connection sc = sig.connect(&slots2);cout << sig.num_slots() << endl;
} // 离开作用域就自动断开了连接
cout << sig.num_slots() << endl;

阻塞连接

Boost提供了一个shared_connection_block实现阻塞和解除阻塞连接的操作,当它被析构(离开作用域)或者被显式的调用unblock()就好解除阻塞;

// 以上省略一些代码
connection c1 = sig.connect(slots1);
connection c2 = sig.connect(slots2);
connection c3 = sig.connect(slots3);
connection c4 = sig.connect(slots4);
sig();
{shared_connection_block block(c1); // 阻塞了c1sig(); //c1不会被调用
}
sig();

触发成员中的槽函数

我们使用signal通常是为了实现类间的通信,实现观察者模式;

我们需要使用bind()函数绑定槽函数,返回函数对象;

#include "boost/signals2.hpp"
#include <iostream>
#include <vector>
using namespace std;
using namespace boost::signals2;class C_Slots1 {
public:int SL(int a) {cout << "slot 1 called" << a << endl;return a;}void SL1(int a, int b) {cout << "slot 2 called " << a << " " << b << endl;}
};int main() {signal<int(int)> sig1;sig1.connect(bind(&C_Slots1::SL, &cs_1,_1)); // 绑定对象的成员signal<void(int, int)>sig2;sig2.connect(bind(&C_Slots1::SL1,&cs_1, _1, _2));cout << *sig1(10) << endl;sig2(1, 2);return 0;
}

自动断开

当槽函数被意外销毁时,信号调用会发生未定义的行为。我们希望它能够跟踪槽函数的生命周期,当槽函数失效时,连接会自动断开;

我们通过boost::shared_ptr来管理槽函数的生命周期,track()函数来跟踪槽所使用的资源;(boost::shared_ptr与std::shared_ptr功能上一样,但是实现不一样,是不一样的!!!)

#include "boost/signals2.hpp"
#include <iostream>
#include <vector>
using namespace std;
using namespace boost::signals2;class C_Slots {
public:int SL(int a) const{cout << "slot 1 called" << a << endl;return a;}
};int main() {typedef signal<int(int)> signal_t;signal_t sig;boost::shared_ptr<C_Slots> p_c1(new C_Slots2());sig5.connect(signal_t::slot_type(&C_Slots::SL, p_c1.get(), _1).track(p_c1));cout << *sig(2) << endl;return 0;
}

boost---signals2使用详解

1、signals2实现了线程安全的“观察者模式”,也称作:信号---插槽,他是一种函数的回调机制,当信号发出时,相应的槽函数会被调用,有点类似于QT中的信号槽。

2、特点:

(1)、一个信号可以与多个插槽函数绑定;

(2)、一个信号与多个插槽函数绑定时,插槽函数可以设置自己被调用的顺序:boost::signals2::at_front、boost::signals2::at_back;

(3)、一个信号与多个插槽函数绑定时,可以对插槽函数进行分组:组号小的函数先调用,组号大的后调用;同一组号中,调用顺序根据boost::signals2::at_back等设置的顺序调用;

(4)、信号与插槽函数一旦断开连接,就不能再次被绑定;

(5)、当前信号绑定的插槽函数数目:num_slots() ,信号是否绑定了插槽函数:empty();

(6)、我们可以通过合并器获取插槽函数被调用后的返回值;

(7)、以下类可以帮助我们进行更加灵活的信号连接管理,boost::signals2::connection:断开连接、判断是否连接;boost::signals2::shared_connection_block:可以阻塞连接、解除阻塞连接、判断当前连接是否阻塞;boost::signals2::scoped_connection:对象析构时会自动释放连接;

// Single_test.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//#include <iostream>
#include <vector>
#include <boost/signals2.hpp>
using namespace std;void func1()
{std::cout << "func1()函数被调用." << std::endl;
}void func2()
{std::cout << "func2()函数被调用." << std::endl;
}void func3()
{std::cout << "func3()函数被调用." << std::endl;
}class my_object
{
public:void operator()(int param){std::cout << "仿函数被调用: " << param << std::endl;}
};//测试组号时用
template<int N>
struct Slot
{void operator()(){std::cout << "Slot current N value is : " << N << std::endl;}
};int func4(int param)
{std::cout << "func3()函数被调用." << std::endl;return param + 10;
}//合并器
template<typename T>
struct Combiner {typedef vector<T> result_type;template<typename InputIterator>result_type operator()(InputIterator first, InputIterator last) const {if (first == last) {return result_type(0);}return result_type(first, last);}
};int func5(int param)
{std::cout << "func5()函数被调用." << std::endl;return param + 5;
}int func6(int param)
{std::cout << "func6()函数被调用." << std::endl;return param + 6;
}int func7(int param)
{std::cout << "func7()函数被调用." << std::endl;return param + 7;
}int func8(int param)
{std::cout << "func8()函数被调用." << std::endl;return param + 8;
}int main()
{//设置槽的调用顺序boost::signals2::signal<void()> sig_void;sig_void.connect(&func1);  sig_void.connect(&func2, boost::signals2::at_front);//将会第一个被调用sig_void();//设置槽带参数boost::signals2::signal<void(int)> sig_obj;sig_obj.connect(my_object());sig_obj(10);//设置组号:根据组号分成组操作,组号小的函数先调用,组号大的后调用;//同一组号,调用顺序根据boost::signals2::at_back等设置的顺序调用boost::signals2::signal<void()> sig_slot;sig_slot.connect(10, Slot<1>());sig_slot.connect(10, Slot<2>());sig_slot.connect(9, Slot<3>());sig_slot.connect(9, Slot<4>(), boost::signals2::at_back);sig_slot.connect(11, Slot<5>());sig_slot.connect(11, Slot<6>());sig_slot.connect(11, Slot<7>(), boost::signals2::at_front);std::cout << "sig_slot信号关联的插槽数量: " << sig_slot.num_slots() << std::endl;if(!sig_slot.empty())sig_slot();//接收槽函数的返回值boost::signals2::signal<int(int)> sig_return;sig_return.connect(&func4);//一个信号对应一个槽函数时可以int m = *sig_return(100);std::cout << m << std::endl;//断开之前所有的信号-槽连接sig_return.disconnect_all_slots();//一个信号对应多个槽函数时:使用合并器获取所有的返回值boost::signals2::signal<int(int), Combiner<int> > sig_ret_combin;sig_ret_combin.connect(&func5);sig_ret_combin.connect(&func6);auto result = sig_ret_combin(200);for (const auto& i : result){std::cout << i << std::endl;}//通过boost::signals::shared_connection_block 管理连接boost::signals2::signal<int(int)> sig_mgr;boost::signals2::shared_connection_block c = sig_mgr.connect(&func7);c.block();//连接被阻塞if (c.blocking())std::cout << "连接被阻塞" << std::endl;sig_mgr(12);//不会被调用c.unblock();//解除阻塞sig_mgr(12);//会被调用//boost::signals::connection管理连接boost::signals2::signal<int(int)> sig_mgr1;boost::signals2::connection c1 = sig_mgr1.connect(&func7);if (c1.connected())sig_mgr1(12);//断开连接c1.disconnect();//boost::signals::scoped_connection管理:析构时会自动释放连接boost::signals2::signal<int(int)> sig_mgr2;{boost::signals2::scoped_connection c2 = sig_mgr2.connect(&func8);std::cout << "当前fun8的连接数: " << sig_mgr2.num_slots() << std::endl;}//连接已被释放,相应槽函数不会被调用sig_mgr2(12);return 1;
}

转载自:https://blog.csdn.net/qq_34347375/article/details/86620845

Boost::signals2 类QT的信号槽实现机制相关推荐

  1. boost::signals2模块实现用于从槽返回值到信号调用的示例程序

    boost::signals2模块实现用于从槽返回值到信号调用的示例程序 实现功能 C++实现代码 实现功能 boost::signals2模块实现用于从槽返回值到信号调用的示例程序 C++实现代码 ...

  2. 学习QT之信号槽机制详解

    学习QT之信号槽机制详解 一.Qt信号槽机制 概念:信号槽是Qt框架引以为豪的机制之一.所谓信号槽,实际就是观察者模式.当某个事件发生之后,比如:按钮检测到自己被点击了一下,它就会发出一个信号(sig ...

  3. boost::signals2模块实现多线程信号调用基准的测试程序

    boost::signals2模块实现多线程信号调用基准的测试程序 实现功能 C++实现代码 实现功能 boost::signals2模块实现多线程信号调用基准的测试程序 C++实现代码 #inclu ...

  4. Qt的信号槽机制介绍(含Qt5与Qt4的差异对比)

    转载地址: https://blog.csdn.net/nicai888/article/details/51169520 一 闲谈: 熟悉Window下编程的小伙伴们,对其消息机制并不陌生, 话说: ...

  5. python简单消息总线实现,类似于C++ Qt的信号槽

    一. 概述 为了模块间解耦,消息总线是常用的方式. 在其它文章中分别提到了lua和C++语言的消息总线的实现 lua语言的消息总线的实现:lua简单消息总线实现,类似于C++ Qt的信号槽 cpp语言 ...

  6. qt 关闭窗口的槽函数_勇哥的VC++应用框架学习之QT(1) 信号槽、按钮控件、opencv读取显示图片...

    前言勇哥对于C语言,C++早些年有一些接触,这个系列贴子就记载一下C++应用框架的学习经验. 在写程序时,UI.基础类库.应用程序框架对于vc来讲,只能依靠MFC和QT了. 勇哥对MFC有很强的抵触, ...

  7. QT Core | 信号槽03 - 自定义信号与槽

    文章目录 一.前言 二.新建一个QT控制台项目 2.1.New File or Project 2.2.Project Location 2.3.Define Build System 2.4.Kit ...

  8. Qt的信号槽机制介绍

    Qt 是一个跨平台的 C++ GUI 应用构架,它提供了丰富的窗口部件集,具有面向对象.易于扩展.真正的组件编程等特点,更为引人注目的是目前 Linux 上最为流行的 KDE 桌面环境就是建立在 Qt ...

  9. QT中信号槽的概念及使用

    文章目录 信号槽的概念 函数原型 信号槽连接的三种方式 方式一 方式二 方式三 参数传递 全局参数 信号槽传参 信号槽的对应关系 总结 信号槽的概念 信号函数与槽函数是 Qt 在 C++ 的基础上新增 ...

最新文章

  1. android 关于字符转化问题
  2. 自制单选多选日历文本框文本域控件
  3. hdu 5497 Inversion(树状数组)
  4. php getbyid,ThinkPHP查询中的魔术方法简述
  5. python构建字典实现英文大写字母与ascii编码的转换_Python:将复杂的字符串字典从Unicode转换为ASCII...
  6. 顺网无盘服务器木马,无盘顺网虚拟服务器设置
  7. 20200210_logistic回归来预测违约的概率
  8. 【张量分析】倒三角 微分算子 对 张量场 的作用
  9. 计算机专业课件ppt背景,ppt背景图片怎么设置
  10. 音频特征----频谱图
  11. Python | 单词搜索(educoder)
  12. c语言表达式必须包含指针类型,c – 错误:表达式必须是指向完整对象类型的指针(?)...
  13. 模拟人生4修改服务器,模拟人生4 全秘籍、作弊码一览 模拟人生4修改方法汇总...
  14. cz.cc免费域名申请教程(因为有朋友不知道怎么操作,特写此简单教程)
  15. 迪斯科算法_为什么迪斯科极乐世界如此可重播
  16. matlab中建立变换器模型,基于Simulink/Matlab的DC-DC变换器系统仿真
  17. AST抽象语法树的基本思想
  18. python绘制小猪佩奇程序设计大作业_代码绘制一只小猪佩奇---python篇
  19. 手把手教你实现TypeScript下的IoC容器
  20. 629 will: 各种用法tyg

热门文章

  1. Centos7 动起来
  2. java复制少文件_Java 复制文件的高效方法
  3. ANTLR学习(二):ANTLR入门项目
  4. linux脚本每月1日跑一次,linux写一个计划脚本每月1号3点重启服务区
  5. mt4如何自动选择最快服务器,【快速查看mt4服务器ip地址----最简单的方法】
  6. 【Python】pandas获取全省人口数据并作可视化分析
  7. 『论文笔记』ACNet: Strengthening the Kernel Skeletons for Powerful CNN via Asymmetric Convolution Blocks!
  8. NDEF消息格式(翻译版)
  9. 牙科电梯的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  10. python中将12345转换为54321