一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - C/C++ - C++模擬實現(xiàn)Qt的信號與槽機制

C++模擬實現(xiàn)Qt的信號與槽機制

2021-12-23 23:05物聯(lián)網(wǎng)培訓(xùn)學(xué)習(xí) C/C++

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

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

C++模擬實現(xiàn)Qt的信號與槽機制

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

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

  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中需要使用信號槽的對象都需要直接或間接繼承一個類QObject,并且需要添加一個私有宏定義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很簡單只有一個普通函數(shù)sendSig()的定義。同理我們再自己定義一個信號的接收者對象和其對應(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;
  18. }

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

然后我們就需要再將發(fā)送者信號與接收者槽關(guān)聯(lián)起來,我們這就提供一個主函數(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;
  11. }

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

我們要想這個程序能正常運行起來,接下來我們就需要去定義一個類似QObject的Object類和上面需要用到的關(guān)鍵字與宏定義,以及模擬MOC預(yù)處理產(chǎn)生對應(yīng)的moc_xx.cpp,里面細節(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. //每個對象可能會有多個信號與槽函數(shù),這里就用兩個vector分別保存信號與槽函數(shù)信息操作起來方便點
  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宏中定義的比較多這里只選擇了我們需使用的幾個
  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. {
  34. X_OBJECT
  35. public:
  36. virtual ~Object() {}
  37. //connect用于建立信號與槽的關(guān)聯(lián)信息
  38. static void connect(Object *sender, const char *s1, Object *receiver, const char *s2);
  39. private:
  40. friend class MetaObject; //用于方便meta對象訪問下面的信號槽map
  41. std::multimap mp; //用于保存信號索引與接收者對象即索引的對應(yīng)關(guān)系
  42. //由于一個信號可以對應(yīng)多個槽,同樣多個信號也可以對應(yīng)一個槽,所以這里選用了multimap容器做對應(yīng)關(guān)系映射
  43. };
  44. object.cpp
  45. #include "object.h"
  46. #include //調(diào)用strcmp函數(shù)需要包含
  47. void MetaObject::activate(Object *sender, int idx, void **argv)
  48. {
  49. //在信號槽對應(yīng)關(guān)系的mp中找到發(fā)送者idx索引信號對應(yīng)的接收者及關(guān)聯(lián)方法的調(diào)用
  50. auto ptr = sender->mp.equal_range(idx);
  51. for(auto it = ptr.first; it != ptr.second; it++) {
  52. MetaObject::Connection con = it->second;
  53. con.m_receiver->metaCall(con.method, argv); //調(diào)用接收者與發(fā)送者信號關(guān)聯(lián)的方法,并傳遞需要的參數(shù)
  54. }
  55. }
  56. void Object::connect(Object *sender, const char *s1, Object *receiver, const char *s2)
  57. {
  58. int sig_idx = -1, slt_idx = -1;
  59. MetaObject *senderMeta = sender->getMetaObject(); //獲取發(fā)送者中保存的meta對象
  60. MetaObject *receiverMeta = receiver->getMetaObject(); //獲取接收中保存的meta對象
  61. //比對信號名稱找到對應(yīng)的信號索引
  62. for(int i = 0; i < senderMeta->sigs.size(); i++) {
  63. if(0 == strcmp(s1+1, senderMeta->sigs[i].c_str())) {
  64. sig_idx = i;
  65. }
  66. }
  67. //這里確認是槽函數(shù),并找到對應(yīng)的槽函數(shù)索引
  68. //如果有信號與信號關(guān)聯(lián)的情況這里就需要去查找接收者對應(yīng)的信號索引,這里省略了
  69. if('1' == *s2) {
  70. for(int i = 0; i < receiverMeta->slts.size(); i++) {
  71. if(0 == strcmp(s2+1, receiverMeta->slts[i].c_str())) {
  72. slt_idx = i;
  73. }
  74. }
  75. }
  76. if(-1 == sig_idx || -1 == slt_idx) {
  77. std::cout << "no match sig or slt" << std::endl;
  78. }
  79. //利用multimap建立信號索引與接收者和方法索引的對應(yīng)關(guān)系
  80. MetaObject::Connection con = {receiver, slt_idx};
  81. sender->mp.insert(std::make_pair(sig_idx, con));
  82. }
  83. //下面的主要是預(yù)留的方便父類調(diào)用子類重寫方法的接口這里簡單定義即可
  84. void Object::metaCall(int idx, void **ag)
  85. {
  86. }
  87. MetaObject Object::meta;
  88. MetaObject *Object::getMetaObject()
  89. {
  90. return &meta;
  91. }

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

  1. moc_sender.cpp
  2. #include "sender.h"
  3. //根據(jù)定義的信號槽順序?qū)⑿盘柵c槽函數(shù)名稱進行保存,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. }
  42. }

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

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

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

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

原文地址:https://www.toutiao.com/a7044711173396218379/

延伸 · 閱讀

精彩推薦
  • C/C++C語言實現(xiàn)電腦關(guān)機程序

    C語言實現(xiàn)電腦關(guān)機程序

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)電腦關(guān)機程序,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    xiaocaidayong8482021-08-20
  • C/C++學(xué)習(xí)C++編程的必備軟件

    學(xué)習(xí)C++編程的必備軟件

    本文給大家分享的是作者在學(xué)習(xí)使用C++進行編程的時候所用到的一些常用的軟件,這里推薦給大家...

    謝恩銘10102021-05-08
  • C/C++詳解c語言中的 strcpy和strncpy字符串函數(shù)使用

    詳解c語言中的 strcpy和strncpy字符串函數(shù)使用

    strcpy 和strcnpy函數(shù)是字符串復(fù)制函數(shù)。接下來通過本文給大家介紹c語言中的strcpy和strncpy字符串函數(shù)使用,感興趣的朋友跟隨小編要求看看吧...

    spring-go5642021-07-02
  • C/C++C/C++經(jīng)典實例之模擬計算器示例代碼

    C/C++經(jīng)典實例之模擬計算器示例代碼

    最近在看到的一個需求,本以為比較簡單,但花了不少時間,所以下面這篇文章主要給大家介紹了關(guān)于C/C++經(jīng)典實例之模擬計算器的相關(guān)資料,文中通過示...

    jia150610152021-06-07
  • C/C++c++ 單線程實現(xiàn)同時監(jiān)聽多個端口

    c++ 單線程實現(xiàn)同時監(jiān)聽多個端口

    這篇文章主要介紹了c++ 單線程實現(xiàn)同時監(jiān)聽多個端口的方法,幫助大家更好的理解和學(xué)習(xí)使用c++,感興趣的朋友可以了解下...

    源之緣11542021-10-27
  • C/C++C++之重載 重定義與重寫用法詳解

    C++之重載 重定義與重寫用法詳解

    這篇文章主要介紹了C++之重載 重定義與重寫用法詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下...

    青山的青6062022-01-04
  • C/C++C語言中炫酷的文件操作實例詳解

    C語言中炫酷的文件操作實例詳解

    內(nèi)存中的數(shù)據(jù)都是暫時的,當(dāng)程序結(jié)束時,它們都將丟失,為了永久性的保存大量的數(shù)據(jù),C語言提供了對文件的操作,這篇文章主要給大家介紹了關(guān)于C語言中文件...

    針眼_6702022-01-24
  • C/C++深入理解goto語句的替代實現(xiàn)方式分析

    深入理解goto語句的替代實現(xiàn)方式分析

    本篇文章是對goto語句的替代實現(xiàn)方式進行了詳細的分析介紹,需要的朋友參考下...

    C語言教程網(wǎng)7342020-12-03
主站蜘蛛池模板: 成人伊在线影院 | 久久婷婷五月免费综合色啪 | 美女曰逼视频 | 国内精品久久久久久不卡影院 | 天天爱天天做天天爽天天躁 | 欧美日韩国产精品自在自线 | 日本孕妇大胆孕交 | tube4欧美4 | 我被黄总征服的全过程 | 二次元美女扒开内裤露尿口 | 果冻传媒新在线观看免费 | 男女福利视频 | 日本伦理动漫在线观看 | 亚洲天堂2016| 国产精品欧美亚洲韩国日本99 | youwu在线影院 | 99视频有精品视频免费观看 | 免费视频一区二区 | 日本中文字幕在线精品 | 欧美又硬又粗又长又大 | 午夜伦伦电影理论片大片 | 国产成人免费在线观看 | 免费国产成人α片 | 日产乱码2021永久手机版 | 热巴在公交车h文 | 蛮荒的童话未删减在线观看 | 超强台风免费观看完整版视频 | 北条麻妃黑人 | 欧美日韩亚洲另类人人澡 | 国产香蕉一区二区精品视频 | 国产在线精品一区二区高清不卡 | 久久精品视在线观看85 | 波多野结衣中文字幕乱七八糟 | 亚洲天堂成人在线观看 | 亚洲精品国产精品麻豆99 | 99热这里只有精品在线播放 | 日韩性公交车上xxhd免费 | 美女奶口隐私免费视频网站 | 精品国产在线观看 | 欧美日韩国产一区二区三区欧 | 久久精品动漫网一区二区 |