c++ 槽函數專題及常見問題 - CSDN

2021-01-10 CSDN技術社區

C++ Boost signal2信號/槽函數

根據網址:Boost C++ 庫 第 4 章 事件處理

http://zh.highscore.de/cpp/boost/eventhandling.html

signals2 基於Boost裡的另一個庫signals,實現了線程安全的觀察者模式。它是一種函數回調機制,當一個信號關聯了多個槽時,信號發出,這些槽將會被調用,當然,也可以僅僅關聯一個槽函數。

其實Qt也提供了它自己的信號和槽機制,那個是非常的靈活和好用的,但是它依賴於Qt的框架,所以退而求其次,選擇了Boost提供了signals2;

signals2庫位於命名空間boost::signals2中,為了使用它,需要包含頭文件<boost/signals2.hpp>;


信號(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;}

 

槽函數是類的成員函數的時候

#include <iostream>#include <boost/signals2.hpp>#include <boost/bind.hpp>#include <boost/function.hpp> using namespace std;using namespace boost; template <typename signature>class Signal{ public: //typedef 信號 typedef boost::signals2::signal<signature> defSignal; typedef typename defSignal::slot_type defSlot; public: //連接槽函數 boost::signals2::connection connectFun(const defSlot& slot); //重載偽函數 void operator()(typename defSignal::template arg<0>::type a0,typename defSignal::template arg<1>::type a1); private: defSignal mSignal; }; //接收信號後響應的函數class FunRecv1{ public: void action(int a, int b){ cout << "add result" << a + b << endl; } }; //接收信號後響應的函數class FunRecv2{ public: void action(int a, int b){ cout << "multi result" << a * b << endl; } }; //實現template <typename signature>boost::signals2::connection Signal<signature>::connectFun(const defSlot& slot){ return mSignal.connect(slot);} template <typename signature>void Signal<signature>::operator()(typename defSignal::template arg<0>::type a0,typename defSignal::template arg<1>::type a1){ mSignal(a0,a1);}void main(){ Signal<void(int,int)> mysignal; FunRecv1 fun1; FunRecv2 fun2; //boost::function<void(int,int)> myfun = boost::bind(&FunRecv1::action,&fun1,_1,_2); //信號連接槽函數 boost::signals2::connection con1 = mysignal.connectFun(boost::bind(&FunRecv1::action,&fun1,100,200)); boost::signals2::connection con2 = mysignal.connectFun(boost::bind(&FunRecv2::action,&fun2,11,22)); mysignal(100,200); con2.disconnect(); mysignal(100,200); cin.get();}

相關焦點

  • 卡方檢驗結果分析專題及常見問題 - CSDN
    簡單統計之後,得到總數和死亡率:此處需要使用到ggbarstats函數,其參數可謂是非常的多,詳細的參數表我放在另一個博客中了,有興趣的朋友可以去看看(https://blog.csdn.net/m0_45248682/article/details/104086729)。
  • c++ 內存,虛函數,運算函數,三角函數
    複合函數:由兩個函數複合而成的複合函數,當裡層的函數是偶函數時,複合函數的偶函數,不論外層是怎樣的函數;當裡層的函數是奇函數、外層的函數也是奇函數時,複合函數是奇函數,當裡層的函數是奇函數、外層的函數是偶函數時,複合函數是偶函數。只有奇奇是奇奇,別的都是偶。
  • bootstrap 有統計圖專題及常見問題 - CSDN
    這裡只是藍圖的結構,主要用到的是views.py視圖函數,models.py定義Dashboard類,提供一些查詢方法,以及模板文件dashboard.html。views.py視圖函數@admin.route('')def dashboard(): group_and_count_users = Dashboard.group_and_count_users() return render_template('admin/page/dashboard.html', group_and_count_users
  • 高中數學:函數的專題知識-關於函數的單調性與最值問題講練PPT
    函數的單調性與最值問題函數的單調性與最值問題函數的單調性與最值問題(3)換元法:對比較複雜的函數可通過換元轉化為熟悉的函數,再用相應的方法求最值.技巧總結歸納:1.函數單調性應用問題的常見類型及解題策略(1)比較大小.比較函數值的大小,應將自變量轉化到同一個單調區間內,然後利用函數的單調性解決.
  • 2021年中考專題複習,二次函數線段、周長最值問題,四種處理思路
    前節提要:2020年中考數學專題複習,幾何最值之將軍飲馬、胡不歸、隱形圓2020年中考專題複習,旋轉之半角模型、手拉手模型、一線三角模型2020年中考數學專題複習,二次函數與三角形面積最值問題,鉛錘法2020年中考數學專題複習,平行四邊形存在性問題
  • t值判斷顯著性專題及常見問題 - CSDN
    提出問題  顯著性值p是一個很常見的事。比如我們在對比兩組或者是多組數據時,對於兩組而言,會做Two Sample T Test,然後給出一個p值,判斷兩者差異的顯著性。在ggplot2中,兩組數據對比時,我們常用的,就是boxplot,那麼,如何在作出兩組或者多組的boxplot的同時,標記出顯著性的數據呢?
  • f p 線性回歸專題及常見問題 - CSDN
    對於一個樣本\(x_i\),它的輸出值是其特徵的線性組合:\[\begin{equation}f(x_i) = \sum_{m=1}^{p}w_m x_{im}+w_0={w}^T{x_i}\end{equation}\]線性回歸的目標是用預測結果儘可能地擬合目標label,用最常見的Least square作為loss function:\[\begin{equation}
  • f檢驗 matlab專題及常見問題 - CSDN
    15.71985 15.91986 15.71987 16.71988 15.31989 16.11990 16.2MATLAB實現參考網上多個代碼可得https://www.ilovematlab.cn/thread-246993-1-1.htmlhttps://blog.csdn.net
  • 線性回歸假設檢驗專題及常見問題 - CSDN
    由於以上兩點原因,所以採用右邊的函數作為損失函數。實際上使用左面的函數定義損失函數也能得到一個線性模型,只是並不是我們常說的線性回歸模型而已。所以:損失函數是機器學習模型的核心。(即使相同的模型形式,不同的損失函數對應著不同模型的效果)。如果把模型看成是人工智慧,那麼損失函數就是它的三觀。
  • a標籤 href vue專題及常見問題 - CSDN
    Dcom.sun.management.jmxremote.port=8999 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremo查詢字符串指的是連結中後邊的問號後的查詢語句,格式為 key=value,多個查詢語句用 & 符號分開.add_query_arg() 函數就是
  • 卡方檢驗相關性專題及常見問題 - CSDN
    互信息卡方檢驗和卡方分布變量關係先從變量之間的關係來說明,變量之間的關係可分為兩類:(1)存在完全確定的關係——稱為函數關係那麼有一個問題:協方差數值大小是否代表了相關程度呢?也就是說如果協方差為 100 是否一定比協方差為 10 的正相關性強呢?假如X1、Y1 和 X2、Y2 分別聯合分布圖,如下所示:
  • 【C++】C++獲取系統日期時間
    ("%d\n",1+p->tm_mon);/*獲取當前月份,範圍是0-11,所以要加1*/ printf("%d\n",1900+p->tm_year);/*獲取當前年份,從1900開始,所以要加1900*/ printf("%d\n",p->tm_yday); /*從今年1月1日算起至今的天數,範圍為0-365*/}原文:https://blog.csdn.net
  • python 顯著性水平專題及常見問題 - CSDN
    R2 = RSS/TSSprint(R2)'''0.987979715684'''T-Distribution統計測驗表明塔的傾斜程度與年份有關係,一個常見的統計顯著性測試是student t-test。這個測試的基礎是T分布,和正態分布很相似,都是鍾型但是峰值較低。
  • pc端的rem布局專題及常見問題 - CSDN
    子元素的float、clear和vertical-align屬性將失效flex布局又叫伸縮布局、彈性布局、伸縮布局、彈性盒子布局採用flex布局的元素稱為flex容器(flex container)簡稱容器,它的所有子元素自動成為容器成員,稱為(flex item)「項目」總之:就是通過給父盒子添加flex屬性,來控制盒子的位置和排列方式父項常見屬性
  • t值 線性回歸專題及常見問題 - CSDN
    xArr); yMat = mat(yArr).T xTx = xMat.T*xMat if linalg.det(xTx) == 0.0: print "This matrix is singular, cannot do inverse" return ws = xTx.I * (xMat.T*yMat) return ws該函數首先讀入
  • android啟動頁設計專題及常見問題 - CSDN
    轉載請註明出處:http://blog.csdn.net/wangjihuanghun/article/details/63255144啟動頁幾乎成為了每個app的標配,有些商家在啟動頁中增加了開屏廣告以此帶來更多的收入。
  • clicked信號 qt 發送者專題及常見問題 - CSDN
    基本概念信號和槽 QObject::connect( &quit, SIGNAL(clicked()), &a, SLOT(quit()) );在兩個Qt對象(直接或間接繼承QObject對象的對象)中建立了一種單向的連接。
  • arduino設置串口模式專題及常見問題 - CSDN
    模擬信號輸出函數 (基於DAC)ESP32提供了兩個DAC通道, 對應引腳25 , 26. 精度為8位.如果超時,該函數將終止。Serial.readString()從Stream實用程序類繼承。句法*Serial*.readString()參量*Serial*:串行埠對象。請參閱「 串行」主頁上每個板的可用串行埠列表。
  • oracle 查詢昨天記錄專題及常見問題 - CSDN
    但是通過dbms_job.run()執行沒有任何問題。這個問題夠鬱悶的,NND,誰曾想Oracle這都成...Oracle最後聲稱:fix made it into 9.2.0.6 patchset在Solaris上的9206尚未發布...暈.好了,就當是個經歷吧,如果有問題非常不可思議的話,那麼大膽懷疑Oracle吧,是Bug,可能就是Bug。
  • 剖析C語言中a=a+++a的無聊問題
    同僚們閒聊,突然就聊到了a+++++a的問題。這種純屬C語言 「二」 級的問題應該是從a+++a引申出來的吧。於是乎兄弟姐妹們開始討論它的運算結果,以及改如何理解。