成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

軍工物聯(lián)網(wǎng)技術(shù):C++模擬實(shí)現(xiàn)Qt的信號與槽機(jī)制

開發(fā) 前端
對于大多學(xué)習(xí)Qt的朋友,心中都有種好奇——那就是Qt最核心的信號與槽是如何實(shí)現(xiàn)的,對于小編自己也是一樣,當(dāng)然大家肯定都會去查閱相關(guān)資料,但大部分時(shí)候也只是一知半解,如果說要自己實(shí)現(xiàn)就會又摸不著頭腦了;所以小編決定自己親自用C++實(shí)現(xiàn)一個(gè)簡單版的信號槽,來理解Qt的實(shí)現(xiàn)原理。

對于大多學(xué)習(xí)Qt的朋友,心中都有種好奇——那就是Qt最核心的信號與槽是如何實(shí)現(xiàn)的,對于小編自己也是一樣,當(dāng)然大家肯定都會去查閱相關(guān)資料,但大部分時(shí)候也只是一知半解,如果說要自己實(shí)現(xiàn)就會又摸不著頭腦了;所以小編決定自己親自用C++實(shí)現(xiàn)一個(gè)簡單版的信號槽,來理解Qt的實(shí)現(xiàn)原理。于是小編就在翻閱各牛人朋友的博客和反復(fù)研究Qt源碼自己重新寫了一下以便交流學(xué)習(xí)。

[[441745]]

我們先還是簡單的梳理一下Qt信號與槽的實(shí)現(xiàn)機(jī)理:在Qt中實(shí)現(xiàn)信號與槽最重要的就是通過元對象系統(tǒng)(MOS)的元對象編譯器(MOC)將我們定義的需要使用到信號與槽的類中的信號及信號調(diào)用槽函數(shù)的方法進(jìn)行定義(這一步就會生成與源文件對應(yīng)的moc_xx.cpp文件),然后通過系統(tǒng)提供的關(guān)聯(lián)方法(connect)將信號與槽建立一一對應(yīng)關(guān)系,當(dāng)發(fā)射信號(其實(shí)就是調(diào)用信號函數(shù))時(shí)就會通過信號與槽的對應(yīng)關(guān)系找到對應(yīng)槽函數(shù)進(jìn)行調(diào)用。這樣的好處就是對于使用者而言不必去關(guān)心函數(shù)指針回調(diào)函數(shù)這些對于初學(xué)者比較不太容易搞清晰的東西,簡化了使用者的操作。當(dāng)然就像我們在享受幸福生活的時(shí)候,就一定有人在我們背后默默付出砥礪前行!這里也一樣,對于我們使用者簡化了操作,那為了實(shí)現(xiàn)這樣的效果就需要在后臺提供更多的支持。接下來我們就通過代碼再來梳理一遍。

首先我們使用信號與槽肯定就會有信號的發(fā)送者與接收者,所以我們就先去定義這兩個(gè)類對象: 

  1. sender.h  
  2. #pragma once  
  3. #include "object.h"  
  4. class Sender : public Object  
  5.  
  6. X_OBJECT  
  7. public 
  8. Sender(int n = 0) : m_num(n){  
  9.  
  10. void sendSig();  
  11. signals:  
  12. void holdClass(int n);  
  13. int m_num;  
  14. };  
  15. sender.cpp  
  16. #include "sender.h"  
  17. void Sender::sendSig()  
  18.  
  19. std::cout << "發(fā)送信號:holdClass" << std::endl;  
  20. emit holdClass(m_num); 
  21.  

在Qt中需要使用信號槽的對象都需要直接或間接繼承一個(gè)類QObject,并且需要添加一個(gè)私有宏定義Q_OBJECT,這里就用Object和X_OBJECT代替,signals是Qt中用于聲明信號函數(shù)的關(guān)鍵字,emit是Qt中用于發(fā)送信號定義的關(guān)鍵字,這里我們先假設(shè)已經(jīng)有這些類和宏定義,注意信號函數(shù)是不需要我們定義的,他是在MOC預(yù)處理生成的moc_xx.cpp中自動生成定義的,所以這里的cpp很簡單只有一個(gè)普通函數(shù)sendSig()的定義。同理我們再自己定義一個(gè)信號的接收者對象和其對應(yīng)的槽函數(shù)。 

  1. receiver.h  
  2. #pragma once  
  3. #include "object.h"  
  4. class Receiver : public Object  
  5.  
  6. X_OBJECT  
  7. public 
  8. Receiver() {  
  9.  
  10. public slots:  
  11. void attendClass(int n);  
  12. };  
  13. receiver.cpp  
  14. #include "receiver.h"  
  15. void Receiver::attendClass(int n)  
  16.  
  17. std::cout << "執(zhí)行槽函數(shù)attendClass:cur class " << n << std::endl;  

這里的slots就是Qt中用于標(biāo)識槽函數(shù)聲明的關(guān)鍵字,槽函數(shù)是需要用戶自己定義的。

然后我們就需要再將發(fā)送者信號與接收者槽關(guān)聯(lián)起來,我們這就提供一個(gè)主函數(shù)來模擬關(guān)聯(lián)信號與槽,讓發(fā)送者產(chǎn)生信號: 

  1. main.cpp  
  2. #include "sender.h"  
  3. #include "receiver.h"  
  4. int main()  
  5.  
  6. Sender xuedao(9527);  
  7. Receiver rjc;  
  8. Object::connect(&xuedao, SIGNAL(holdClass(int)), &rjc, SLOT(attendClass(int)));  
  9. xuedao.sendSig();  
  10. return 0;  

這里的SIGNAL與SLOT在Qt中就是兩個(gè)轉(zhuǎn)換字符串的宏定義,connect是QObject的一個(gè)靜態(tài)函數(shù)方法。

我們要想這個(gè)程序能正常運(yùn)行起來,接下來我們就需要去定義一個(gè)類似QObject的Object類和上面需要用到的關(guān)鍵字與宏定義,以及模擬MOC預(yù)處理產(chǎn)生對應(yīng)的moc_xx.cpp,里面細(xì)節(jié)的地方為了方便理解我都通過代碼注釋解釋說明了 

  1. object.h  
  2. #pragma once  
  3. #include  
  4. #include  
  5. #include  
  6. #include  
  7. #define signals protected  
  8. #define slots  
  9. #define emit 
  10.  #define SLOT(slt) "1"#slt // 1用于標(biāo)識槽函數(shù)  
  11. #define SIGNAL(sig) "2"#sig //2用于標(biāo)識信號  
  12. class Object; 
  13.  struct MetaObject  
  14.  
  15. //每個(gè)對象可能會有多個(gè)信號與槽函數(shù),這里就用兩個(gè)vector分別保存信號與槽函數(shù)信息操作起來方便點(diǎn)  
  16. std::vector sigs;  
  17. std::vector slts;  
  18. //activate的功能是通過信號發(fā)送者即信號索引找到關(guān)聯(lián)接收者和方法索引并調(diào)用對應(yīng)方法  
  19. static void activate(Object *sender, int idx, void **argv); //void **argv對應(yīng)信號傳遞的參數(shù)  
  20. struct Connection //用于打包信號接收者與方法的索引(對應(yīng)上面定義的vector中的信號槽的索引) 
  21.   
  22. Object *m_receiver;  
  23. int method;  
  24. };  
  25. };  
  26. //Q_OBJECT宏中定義的比較多這里只選擇了我們需使用的幾個(gè)  
  27. //static MetaObject meta用于保存使用該宏定義對象中的信號與槽信號與槽的相關(guān)信息  
  28. //getMetaObject()用于返回發(fā)送者或接收者對象中的static MetaObject meta對象 
  29.  #define X_OBJECT static MetaObject meta; \  
  30. virtual MetaObject *getMetaObject(); \  
  31. virtual void metaCall(int idx, void **argv); //idx為對應(yīng)槽函數(shù)的索引,void**argv用于接收信號傳遞的參數(shù)  
  32. class Object //需要使用信號槽對象的公共接口對象  
  33.  X_OBJECT  
  34. public 
  35. virtual ~Object() {}  
  36. //connect用于建立信號與槽的關(guān)聯(lián)信息  
  37. static void connect(Object *sender, const char *s1, Object *receiver, const char *s2);  
  38. private: 
  39.  
  40. friend class MetaObject; //用于方便meta對象訪問下面的信號槽map  
  41. std::multimap mp; //用于保存信號索引與接收者對象即索引的對應(yīng)關(guān)系  
  42. //由于一個(gè)信號可以對應(yīng)多個(gè)槽,同樣多個(gè)信號也可以對應(yīng)一個(gè)槽,所以這里選用了multimap容器做對應(yīng)關(guān)系映射  
  43. }; 
  44.  
  45. object.cpp  
  46. #include "object.h"  
  47. #include //調(diào)用strcmp函數(shù)需要包含  
  48. void MetaObject::activate(Object *sender, int idx, void **argv)  
  49.  //在信號槽對應(yīng)關(guān)系的mp中找到發(fā)送者idx索引信號對應(yīng)的接收者及關(guān)聯(lián)方法的調(diào)用 
  50.  
  51. auto ptr = sender->mp.equal_range(idx);  
  52. for(auto it = ptr.first; it != ptr.second; it++) {  
  53. MetaObject::Connection con = it->second
  54.  con.m_receiver->metaCall(con.method, argv); //調(diào)用接收者與發(fā)送者信號關(guān)聯(lián)的方法,并傳遞需要的參數(shù)  
  55.  
  56.  
  57. void Object::connect(Object *sender, const char *s1, Object *receiver, const char *s2)  
  58.  
  59. int sig_idx = -1, slt_idx = -1;  
  60. MetaObject *senderMeta = sender->getMetaObject(); //獲取發(fā)送者中保存的meta對象  
  61. MetaObject *receiverMeta = receiver->getMetaObject(); //獲取接收中保存的meta對象  
  62. //比對信號名稱找到對應(yīng)的信號索引 
  63.  
  64. for(int i = 0; i < senderMeta->sigs.size(); i++) {  
  65. if(0 == strcmp(s1+1, senderMeta->sigs[i].c_str())) {  
  66. sig_idx = i; 
  67.  
  68.  
  69.  
  70. //這里確認(rèn)是槽函數(shù),并找到對應(yīng)的槽函數(shù)索引  
  71. //如果有信號與信號關(guān)聯(lián)的情況這里就需要去查找接收者對應(yīng)的信號索引,這里省略了  
  72. if('1' == *s2) { 
  73.  for(int i = 0; i < receiverMeta->slts.size(); i++) {  
  74. if(0 == strcmp(s2+1, receiverMeta->slts[i].c_str())) {  
  75. slt_idx = i; 
  76.  
  77.  
  78.  
  79.  
  80. if(-1 == sig_idx || -1 == slt_idx) { 
  81.  
  82. std::cout << "no match sig or slt" << std::endl;  
  83.  
  84. //利用multimap建立信號索引與接收者和方法索引的對應(yīng)關(guān)系  
  85. MetaObject::Connection con = {receiver, slt_idx};  
  86. sender->mp.insert(std::make_pair(sig_idx, con));  
  87.  
  88. //下面的主要是預(yù)留的方便父類調(diào)用子類重寫方法的接口這里簡單定義即可  
  89. void Object::metaCall(int idx, void **ag) 
  90.  
  91.  
  92.  
  93. MetaObject Object::meta;  
  94. MetaObject *Object::getMetaObject()  
  95.  
  96. return &meta;  

下面就輪到MOC生成的moc_xx.cpp,這些文件在Qt中是自動生成的不需要我們實(shí)現(xiàn),我這里只能手動模擬簡單的實(shí)現(xiàn)發(fā)送者的moc_sender.cpp與接收者的moc_receiver.cpp最終我們編譯程序是需要將這兩個(gè)文件一起編譯才能通過的。 

  1. moc_sender.cpp  
  2. #include "sender.h"  
  3. //根據(jù)定義的信號槽順序?qū)⑿盘柵c槽函數(shù)名稱進(jìn)行保存,Qt中會將函數(shù)名稱參數(shù)分開保存處理,這里簡單模擬以下就好  
  4. static const char *sigs_name[] = {"holdClass(int)"};  
  5. static const char *slts_name[] = {nullptr}; //空表示當(dāng)前沒有定義對應(yīng)的函數(shù)  
  6. static std::vector sigs(sigs_name, sigs_name+1);  
  7. static std::vector slts;  
  8. MetaObject Sender::meta = {sigs, slts};  
  9. //Sender的信號定義  
  10. void Sender::holdClass(int n)  
  11.  
  12. void *arg[] = {(void *)&n};  
  13. //調(diào)用MetaObject的靜態(tài)方法activate傳遞當(dāng)前的信號發(fā)送者對象、信號索引及參數(shù)  
  14. MetaObject::activate(this, 0, arg); //0表示當(dāng)前信號函數(shù)在sigs_name[]中的索引 
  15.   
  16. MetaObject *Sender::getMetaObject()  
  17.  
  18. return &meta; //返回Sender的meta對象  
  19.  
  20. void Sender::metaCall(int idx, void **arg)  
  21.  
  22. // 我們這里Sender 中沒有槽函數(shù)所以這里沒任何操作  
  23.  
  24. moc_receiver.cpp 
  25.  #include "receiver.h"  
  26. static const char *sigs_name[] = {nullptr};  
  27. static const char *slts_name[] = {"attendClass(int)"};  
  28. static std::vector sigs;  
  29. static std::vector slts(slts_name, slts_name+1);  
  30. MetaObject Receiver::meta = {sigs, slts};  
  31. MetaObject *Receiver::getMetaObject()  
  32.  
  33. return &meta; //返回Receiver的meta對象  
  34.  
  35. void Receiver::metaCall(int idx, void **arg)  
  36.  
  37. //這里根據(jù)slts_name[]中的索引值調(diào)用對應(yīng)的槽函數(shù)  
  38. if(0 == idx) {  
  39. int n = *((int *)arg[0]);  
  40. attendClass(n);  
  41.  

有了上面這些文件最后我們只需要將所有的.cpp文件一起編譯運(yùn)行就可以實(shí)現(xiàn)Qt中信號與槽的效果了:

  1. g++ object.cpp sender.cpp receiver.cpp moc_sender.cpp moc_receiver.cpp main.cpp -o xuedao 

也可用其他可使用的編譯器編譯進(jìn)行編譯,這里直接用的g++。 

另外如果某個(gè)對象修改或增刪了信號或槽就需要去手動修改對應(yīng)的moc_xx.cpp文件即可,Qt中實(shí)現(xiàn)考慮的實(shí)際問題會更多,這里只是把整個(gè)信號槽關(guān)聯(lián)及調(diào)用流程框架進(jìn)行了梳理,具體的大家可以參考Qt源碼做深入學(xué)習(xí)。

 

責(zé)任編輯:未麗燕 來源: 今日頭條
相關(guān)推薦

2011-06-09 09:45:35

Linux QT 信號

2011-06-13 10:21:25

QT 信號 槽機(jī)制

2011-06-23 14:40:13

Qt 信號

2011-06-15 14:38:01

QT 信號

2011-02-22 16:47:34

物聯(lián)網(wǎng)組網(wǎng)

2021-03-23 09:36:33

物聯(lián)網(wǎng)開源技術(shù)

2021-03-09 10:30:26

物聯(lián)網(wǎng)技術(shù)物聯(lián)網(wǎng)IOT

2022-09-19 23:20:09

物聯(lián)網(wǎng)工業(yè)4.0工業(yè)物聯(lián)網(wǎng)

2023-11-02 11:59:04

物聯(lián)網(wǎng)預(yù)測維護(hù)

2023-08-17 11:32:07

2018-06-28 23:03:10

物聯(lián)網(wǎng)云梯電梯

2020-11-03 13:35:06

物聯(lián)網(wǎng)

2024-05-07 10:27:19

2018-07-04 13:24:47

物聯(lián)網(wǎng)IOT智能交通

2019-07-09 13:12:15

工業(yè)物聯(lián)網(wǎng)技術(shù)IIoT物聯(lián)網(wǎng)

2021-11-16 15:38:40

物聯(lián)網(wǎng)IOT人工智能

2019-07-30 07:29:03

2020-04-30 21:09:16

物聯(lián)網(wǎng)邊緣計(jì)算技術(shù)

2023-08-01 10:09:59

物聯(lián)網(wǎng)IoT

2021-05-02 23:42:25

物聯(lián)網(wǎng)農(nóng)業(yè)技術(shù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 在线午夜 | 午夜爽爽爽男女免费观看影院 | 婷婷开心激情综合五月天 | 国产高清免费视频 | 日本中文在线 | 久久精品一区二区三区四区 | 久久国内精品 | 久久精品国产99国产 | 成人免费在线观看视频 | 欧美成人精品一区二区男人看 | 国产精品乱码一区二三区小蝌蚪 | 久草.com | 日韩一区二区三区在线视频 | 国产一区二区三区不卡av | 在线午夜电影 | 久久99精品久久久 | 网站黄色在线 | 一级黄色夫妻生活 | 国产午夜亚洲精品不卡 | 五月花丁香婷婷 | 日本视频一区二区 | 亚洲激情第一页 | 日韩欧美国产精品 | 亚洲精品一区二区另类图片 | 成人av一区二区亚洲精 | 99精品免费久久久久久日本 | 久久久久国产一区二区三区四区 | 99久久精品免费看国产四区 | 久热中文字幕 | 久久精品女人天堂av | 国产成人精品一区二区三区四区 | 91视频免费 | 久久se精品一区精品二区 | 亚洲视频www | 免费超碰 | av在线黄 | 亚洲欧洲在线看 | 在线免费毛片 | 久久天天躁狠狠躁夜夜躁2014 | 91精品国产一区二区三区 | 国产网站在线免费观看 |