久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

Qt的slot和singnal的本質(zhì)

 懶人海馬 2023-02-27 發(fā)布于山東

 Qt的slot的signal是一種對(duì)象之間的通信方式,,在講這個(gè)之前,,要講一下Qt的元對(duì)象系統(tǒng)


Qt的元對(duì)象系統(tǒng) Meta Object System

Qt的元對(duì)象系統(tǒng) Meta Object System 主要是分成這個(gè)幾個(gè)部分

(1)所有的類都是QObject的的子類

(2)Q_OBJECT的宏定義會(huì)激活元對(duì)象的功能,例如說(shuō)信號(hào)和槽

(3)如果定義了 Q_OBJECT  宏定義,,會(huì)讓moc編輯器自動(dòng)生成一些代碼和類來(lái)支持元對(duì)象的功能,。

至少是讓該類產(chǎn)生了這么幾個(gè)數(shù)據(jù)

    static const QMetaObject  staticMetaObject;     virtual const QMetaObject *metaObject() const;     virtual void *qt_metacast(const char *);     virtual int qt_metacall(QMetaObject::Call, int, void **); 
    static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **)(Q5.12.4)

      (1)其中QMetaObject靜態(tài)變量staticMetaObject包含了QObject的所謂的元數(shù)據(jù),也就是QObject信息的一些描述信息:除了類型信息外,,還包含QT中特有的signal&slot信息,。這些信息是整個(gè)類共享的,所用用static來(lái)申明,。                

  1. struct Q_CORE_EXPORT QMetaObject
  2. {
  3. // ......
  4. struct { // private data
  5. const QMetaObject *superdata;
  6. const char *stringdata;
  7. const uint *data;
  8. const void *extradata;
  9. } d;
  10. };

       上面是QMetaObject的申明,,就是這些數(shù)據(jù)成員記錄了所有的signal、slot,、property,、class information信息。

  •       const QMetaObject *superdata,,該變量指向與之對(duì)應(yīng)的QObject類的父類對(duì)象,,或者是祖先類的QMetaObject對(duì)象。每一個(gè)QObject類或者其派生類可能有一個(gè)父類或者父類的父類,。那么superdata就是指向與其最接近的祖先類中的QMetaObject對(duì)象,。如果沒(méi)有父類,那么該變量就是一個(gè)NULL指針,。
  • const char*stringdata,,這是一個(gè)指向string data的指針。但它和我們平時(shí)所使用的一般的字符串指針卻很不一樣,,我們平時(shí)使用的字符串指針只是指向一個(gè)字符串的指針,,而這個(gè)指針卻指向的是很多個(gè)字符串。那么它不就是字符串?dāng)?shù)組嗎,?哈哈,,也不是,。因?yàn)镃++的字符串?dāng)?shù)組要求數(shù)組中的每一個(gè)字符串擁有相同的長(zhǎng)度,這樣才能組成一個(gè)數(shù)組,。那它是不是一個(gè)字符串指針數(shù)組呢,?也不是,那它到底是什么呢,?讓我們來(lái)看一看它的具體值,,以QObject這個(gè)class的QMetaObject為例來(lái)說(shuō)明。
    1. static const char qt_meta_stringdata_QObject[] =
    2. {
    3. "QObject\0\0destroyed(QObject*)\0destroyed()\0"
    4. "deleteLater()\0_q_reregisterTimers(void*)\0"
    5. "QString\0objectName\0parent\0QObject(QObject*)\0"
    6. "QObject()\0"
    7. };

    這個(gè)字符串都是些什么內(nèi)容呀,?有,,Class Name, Signal Name,Slot Name, Property Name??吹竭@些大家是不是覺(jué)得很熟悉呀,,對(duì)啦,他們就是MetaSystem所支持的最核心的功能屬性了,。

  • const unit *data,,這個(gè)指針指向一個(gè)正整數(shù)數(shù)組,只不過(guò)在不同的object中數(shù)據(jù)的長(zhǎng)度不一定相同,,這取決于與之相應(yīng)的class中定義了多少signal,、slot和property。

    這個(gè)整數(shù)數(shù)組的值,,有一部分指出了前一個(gè)變量(stringdata)中不同字符串的索引值,,但是需要注意的是,這里面的數(shù)值并不是直接標(biāo)明了每一個(gè)字符串的索引值,,這個(gè)數(shù)組還需要通過(guò)一個(gè)相應(yīng)的算法計(jì)算后,,才能獲得正確的字符串的索引值。

    1. static const uint qt_meta_data_QObject[] =
    2. {
    3. //content:
    4. 2, //revision
    5. 0, //classname
    6. 0, 0, //classinfo
    7. 4, 12, //methods
    8. 1, 32, //properties
    9. 0, 0, //enums/sets
    10. 2, 35, //constructors
    11. //signals:signature,parameters,type,tag,flags
    12. 9, 8, 8, 8, 0x05,
    13. 29, 8, ,8 8, 0x25,
    14. //slots:signature,parameters,type,tag,flags
    15. 41, 8, 8, 8, 0x0a,
    16. 55, 8, 8, 8, 0x08,
    17. //properties:name,type,flags
    18. 90, 82, 0x0a095103,
    19. //constructors:signature,parameters,type,tag,flags
    20. 108, 101, 8, 8, 0x0e,
    21. 126, 8, 8, 8, 0x2e,
    22. 0 //end
    23. };

    第一個(gè)section,,就是 //content 區(qū)域的整數(shù)值,這一塊區(qū)域在每一個(gè)QMetaObject的實(shí)體對(duì)象中數(shù)量都是相同的,,含義也相同,,但具體的值就不同了。專門有一個(gè)struct定義了這個(gè)section,,其含義在上面的注釋中已經(jīng)說(shuō)的很清楚了,。第二個(gè)section,以 // signals 開頭的這段,。這個(gè)section中的數(shù)值指明了QObject這個(gè)class包含了兩個(gè)signal,,第三個(gè)section,以 // slots 開頭的這段,。這個(gè)section中的數(shù)值指明了QObject這個(gè)class包含了兩個(gè)slot,。第四個(gè)section,,以 // properties 開頭的這段。這個(gè)section中的數(shù)值指明了QObject這個(gè)class包含有一個(gè)屬性定義,。第五個(gè)section,,以 // constructors 開頭的這段,指明了QObject這個(gè)class有兩個(gè)constructor,。

     (2)   virtual QObject::metaObject(); 這個(gè)的方法是返回一個(gè)該類對(duì)應(yīng)的staticMetaObject的const 指針,。也就是引用。

如果說(shuō)這個(gè)類沒(méi)有定義Q_OBJECT 宏定義,,那么這個(gè)類就沒(méi)有staticMetaObject這個(gè)靜態(tài)的QMetaObject對(duì)象,,那么這個(gè)時(shí)候這實(shí)例調(diào)用返回是是其父類的QMetaObject對(duì)象,因?yàn)檫@個(gè)虛函數(shù)沒(méi)有被重載,。所以一般建議都加上這個(gè)宏定義,。

      (3)qt_static_metacall()是一個(gè)靜態(tài)函數(shù),主要是用于對(duì)象的函數(shù)的調(diào)用的,。Qt 5.12版本或少以上,,這個(gè)靜態(tài)函數(shù)也會(huì)通過(guò)指針的方式存儲(chǔ)在QMetaObject 里面了。qt_static_metacall()提供了兩種的函數(shù)調(diào)用的方式,,一個(gè)是InvokeMetaMethod調(diào)用,,會(huì)將對(duì)象轉(zhuǎn)換為實(shí)際的對(duì)象類型,然后調(diào)用,。另外一個(gè)是IndexOfMethod調(diào)用,,會(huì)通過(guò)元函數(shù)的索引號(hào)調(diào)用。

 


Qt的slot和singnal

qt的slot和singnal的名稱都會(huì)存在的QMetaObject中,,但是這個(gè)時(shí)候,,slot和signal是沒(méi)有聯(lián)系的。需要進(jìn)行connect才可以使用,。

 例如:connect(ui->pushBtn,SIGNAL(clicked()),ui->lineEdit,SLOT(clear()));

 connect 在幕后到底都做了些什么事情,?為什么emit一個(gè)signal后,相應(yīng)的slot都會(huì)被調(diào)用,?

SIGNAL和SLOT宏定義

connect(&obj, SIGNAL(destroyed()), &app, SLOT(aboutQt()));

在這里signal和slot的名字都被包含在了兩個(gè)大寫的SIGNAL和SLOT中,,這兩個(gè)是什么呢?原來(lái)SIGNAL 和 SLOT 是Qt定義的兩個(gè)宏,。

# define SLOT(a)      ”1″#a  
# define SIGNAL(a)   ”2″#a  

Qt把signal和slot都轉(zhuǎn)化成了字符串,,并且還在這個(gè)字符串的前面加上了附加的符號(hào),signal前面加了’2’,,slot前面加了’1’,。也就是說(shuō),我們前面寫了下面的connect調(diào)用,在經(jīng)過(guò)moc編譯器轉(zhuǎn)換之后,,就便成了:
connect(&obj, “2destroyed()”, &app, “1aboutQt()”)); 當(dāng)connect函數(shù)被調(diào)用了之后,,都會(huì)去檢查這兩個(gè)參數(shù)是否是使用這兩個(gè)宏正確的轉(zhuǎn)換而來(lái)的,它檢查的根據(jù)就是這兩個(gè)前置數(shù)字,,是否等于1或者是2,,如果不是,connect函數(shù)當(dāng)然就會(huì)失敗啦,!

 然后,,會(huì)去檢查發(fā)送signal的對(duì)象是否有這個(gè)signal,方法就是查找這個(gè)對(duì)象的class所對(duì)應(yīng)的staticMetaObject對(duì)象中所包含的d.stringdata所指向的字符串中是否包含這個(gè)signal的名字,,在這個(gè)檢查過(guò)程中,,就會(huì)用到d.data所指向的那一串整數(shù),通過(guò)這些整數(shù)值來(lái)計(jì)算每一個(gè)具體字符串的起始地址,。同理,,還會(huì)使用同樣的方法去檢查slot,看響應(yīng)這個(gè)signal的對(duì)象是否包含有相應(yīng)的slot,。這兩個(gè)檢查的任何一個(gè)如果失敗的話,,connect函數(shù)就失敗了,返回false,。

前面的步驟都是在做一些必要的檢查工作,,下一步,就是要把發(fā)送signal的對(duì)象和響應(yīng)signal的對(duì)象關(guān)聯(lián)起來(lái),。在QObject的私有數(shù)據(jù)類QObjectPrivate中,,有下面這些數(shù)據(jù)結(jié)構(gòu)來(lái)保存這些信息:

  1. class QObjectPrivate : public QObjectData
  2. {
  3.   struct Connection
  4.   {
  5.     QObject *receiver;
  6.     int method;
  7.     uint connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking
  8.     QBasicAtomicPointer<int> argumentTypes;
  9.   };
  10.   typedef QList<Connection>; ConnectionList;
  11.   QObjectConnectionListVector *connectionLists;
  12.   struct Sender
  13.   {
  14.     QObject *sender;
  15.     int signal;
  16.     int ref;
  17.   };
  18.   QList<Sender> senders;
  19. };

    在發(fā)送signal的對(duì)象中,每一個(gè)signal和slot的connection,,都會(huì)創(chuàng)建一個(gè)QObjectPrivate::Connection對(duì)象,,并且把這個(gè)對(duì)象保存到connectionList這個(gè)Vector里面去。

   在響應(yīng)signal的對(duì)象中,,同樣,,也是每一個(gè)signal和slot的connection,都會(huì)一個(gè)創(chuàng)建一個(gè)Sender對(duì)象,,并且把這個(gè)對(duì)象附加在Senders這個(gè)列表中,。

 

emit幕后的故事

當(dāng)emit signal的時(shí)候,與這個(gè)signal相連的slot函數(shù)就會(huì)被調(diào)用,,那么這個(gè)調(diào)用時(shí)如何發(fā)生的呢?

看段代碼:

  1. class ZMytestObj : public QObject
  2. {
  3.   Q_OBJECT
  4.   signals:
  5.   void sigMenuClicked();
  6.   void sigBtnClicked();
  7. };
  1. void ZMytestObj::sigMenuClicked()
  2. {
  3.   QMetaObject::activate(this,&staticMetaObject,0,0);
  4. }
  5. void ZMytestObj::sigBtnClicked()
  6. {
  7.   QMetaObject::activate(this,&staticMetaObject,1,0);
  8. }

   每一個(gè)signal都會(huì)被轉(zhuǎn)換為一個(gè)與之相對(duì)應(yīng)的成員函數(shù),。也就是說(shuō),,當(dāng)我們寫下這樣一行代碼:emit sigBtnClicked();當(dāng)程序運(yùn)行到這里的時(shí)候,實(shí)際上就是調(diào)用了void ZMytestObj::sigBtnClicked() 這個(gè)函數(shù),。

  void ZMytestObj::sigMenuClicked()  void ZMytestObj::sigBtnClicked(),,它們唯一的區(qū)別就是調(diào)用 QMetaObject::activate 函數(shù)時(shí)給出的參數(shù)不同,,一個(gè)是0,一個(gè)是1,,它們的含義是什么呢,?它們表示是這個(gè)類中的第幾個(gè)signal被發(fā)送出來(lái)了,回頭再去看頭文件就會(huì)發(fā)現(xiàn)它們就 是在這個(gè)類定義中,,signal定義出現(xiàn)的順序,,這個(gè)參數(shù)可是非常重要的,它直接決定了進(jìn)入這個(gè)函數(shù)體之后所發(fā)生的事情,。

當(dāng)執(zhí)行流程進(jìn)入到QMetaObject::activate函數(shù)中后,,會(huì)先從connectionLists這個(gè)變量中取出與這個(gè)signal相對(duì)應(yīng)的connection list,它根據(jù)的就是剛才所傳入進(jìn)來(lái)的signal index,。這個(gè)connection list中保存了所有和這個(gè)signal相鏈接的slot的信息,,每一對(duì)connection(即:signal和slot的連接)是這個(gè)list中的一項(xiàng)。

在每個(gè)一具體的鏈接記錄中,,還保存了這個(gè)鏈接的類型,,是自動(dòng)鏈接類型,還是隊(duì)列鏈接類型,,或者是阻塞鏈接類型,,不同的類型處理方法還不一樣的。

  1. if((c->connectionType == Qt::AutoConnection && !receiverInSameThread)
  2. || (c->connectionType == Qt::QueuedConnection)) {
  3. // 隊(duì)列處理
  4. } else if (c->connectionType == Qt::BlockingQueuedConnection) {
  5. // 阻塞處理
  6. // 如果同線程,,打印潛在死鎖,。
  7. } else {
  8. //直接調(diào)用槽函數(shù)或回調(diào)函數(shù)
  9. }

    如果信號(hào)-槽連接方式為QueuedConnection,不論是否在同一個(gè)線程,,按隊(duì)列處理,。如果信號(hào)-槽連接方式為Auto,且不在同一個(gè)線程,,也按隊(duì)列處理,。

  如果信號(hào)-槽連接方式為阻塞隊(duì)列BlockingQueuedConnection,按阻塞處理,。(注意同一個(gè)線程就不要按阻塞隊(duì)列調(diào)用了,。因?yàn)橥粋€(gè)線程,同時(shí)只能做一件事,,本身就是阻塞的,,直接調(diào)用就好了,如果走阻塞隊(duì)列,,則多了加鎖的過(guò)程,。如果槽中又發(fā)了同樣的信號(hào),就會(huì)出現(xiàn)死鎖:加鎖之后還未解鎖,又來(lái)申請(qǐng)加鎖,。)

   隊(duì)列處理,,就是把槽函數(shù)的調(diào)用,轉(zhuǎn)化成了QMetaCallEvent事件,,通過(guò)QCoreApplication::postEvent

   放進(jìn)了事件循環(huán), 等到下一次事件分發(fā),,相應(yīng)的線程才會(huì)去調(diào)用槽函數(shù)。

    對(duì)于直接鏈接的類型,,先找到接收這個(gè)signal的對(duì)象的指針,,然后是處理這個(gè)signal的slot的index,已經(jīng)是否有需要處理的參數(shù),,然后就使用這些信息去調(diào)用receiver的qt_metcall 方法,。在qt_metcall方法中就簡(jiǎn)單了,根據(jù)slot的index,,一個(gè)大switch語(yǔ)句,,調(diào)用相應(yīng)的slot函數(shù)就OK了。 Qt的slot的signal是一種對(duì)象之間的通信方式,,在講這個(gè)之前,,要講一下Qt的元對(duì)象系統(tǒng)


Qt的元對(duì)象系統(tǒng) Meta Object System

Qt的元對(duì)象系統(tǒng) Meta Object System 主要是分成這個(gè)幾個(gè)部分

(1)所有的類都是QObject的的子類

(2)Q_OBJECT的宏定義會(huì)激活元對(duì)象的功能,例如說(shuō)信號(hào)和槽

(3)如果定義了 Q_OBJECT  宏定義,,會(huì)讓moc編輯器自動(dòng)生成一些代碼和類來(lái)支持元對(duì)象的功能,。

至少是讓該類產(chǎn)生了這么幾個(gè)數(shù)據(jù)

    static const QMetaObject  staticMetaObject;     virtual const QMetaObject *metaObject() const;     virtual void *qt_metacast(const char *);     virtual int qt_metacall(QMetaObject::Call, int, void **); 
    static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **)(Q5.12.4)

      (1)其中QMetaObject靜態(tài)變量staticMetaObject包含了QObject的所謂的元數(shù)據(jù),也就是QObject信息的一些描述信息:除了類型信息外,,還包含QT中特有的signal&slot信息,。這些信息是整個(gè)類共享的,所用用static來(lái)申明,。                

  1. struct Q_CORE_EXPORT QMetaObject
  2. {
  3. // ......
  4. struct { // private data
  5. const QMetaObject *superdata;
  6. const char *stringdata;
  7. const uint *data;
  8. const void *extradata;
  9. } d;
  10. };

       上面是QMetaObject的申明,,就是這些數(shù)據(jù)成員記錄了所有的signal、slot,、property,、class information信息。

  •       const QMetaObject *superdata,,該變量指向與之對(duì)應(yīng)的QObject類的父類對(duì)象,,或者是祖先類的QMetaObject對(duì)象。每一個(gè)QObject類或者其派生類可能有一個(gè)父類或者父類的父類,。那么superdata就是指向與其最接近的祖先類中的QMetaObject對(duì)象,。如果沒(méi)有父類,那么該變量就是一個(gè)NULL指針,。
  • const char*stringdata,,這是一個(gè)指向string data的指針,。但它和我們平時(shí)所使用的一般的字符串指針卻很不一樣,我們平時(shí)使用的字符串指針只是指向一個(gè)字符串的指針,,而這個(gè)指針卻指向的是很多個(gè)字符串。那么它不就是字符串?dāng)?shù)組嗎,?哈哈,,也不是。因?yàn)镃++的字符串?dāng)?shù)組要求數(shù)組中的每一個(gè)字符串擁有相同的長(zhǎng)度,,這樣才能組成一個(gè)數(shù)組,。那它是不是一個(gè)字符串指針數(shù)組呢?也不是,,那它到底是什么呢,?讓我們來(lái)看一看它的具體值,以QObject這個(gè)class的QMetaObject為例來(lái)說(shuō)明,。
    1. static const char qt_meta_stringdata_QObject[] =
    2. {
    3. "QObject\0\0destroyed(QObject*)\0destroyed()\0"
    4. "deleteLater()\0_q_reregisterTimers(void*)\0"
    5. "QString\0objectName\0parent\0QObject(QObject*)\0"
    6. "QObject()\0"
    7. };

    這個(gè)字符串都是些什么內(nèi)容呀,?有,Class Name, Signal Name,Slot Name, Property Name,??吹竭@些大家是不是覺(jué)得很熟悉呀,對(duì)啦,,他們就是MetaSystem所支持的最核心的功能屬性了,。

  • const unit *data,這個(gè)指針指向一個(gè)正整數(shù)數(shù)組,,只不過(guò)在不同的object中數(shù)據(jù)的長(zhǎng)度不一定相同,,這取決于與之相應(yīng)的class中定義了多少signal、slot和property,。

    這個(gè)整數(shù)數(shù)組的值,,有一部分指出了前一個(gè)變量(stringdata)中不同字符串的索引值,但是需要注意的是,,這里面的數(shù)值并不是直接標(biāo)明了每一個(gè)字符串的索引值,,這個(gè)數(shù)組還需要通過(guò)一個(gè)相應(yīng)的算法計(jì)算后,才能獲得正確的字符串的索引值,。

    1. static const uint qt_meta_data_QObject[] =
    2. {
    3. //content:
    4. 2, //revision
    5. 0, //classname
    6. 0, 0, //classinfo
    7. 4, 12, //methods
    8. 1, 32, //properties
    9. 0, 0, //enums/sets
    10. 2, 35, //constructors
    11. //signals:signature,parameters,type,tag,flags
    12. 9, 8, 8, 8, 0x05,
    13. 29, 8, ,8 8, 0x25,
    14. //slots:signature,parameters,type,tag,flags
    15. 41, 8, 8, 8, 0x0a,
    16. 55, 8, 8, 8, 0x08,
    17. //properties:name,type,flags
    18. 90, 82, 0x0a095103,
    19. //constructors:signature,parameters,type,tag,flags
    20. 108, 101, 8, 8, 0x0e,
    21. 126, 8, 8, 8, 0x2e,
    22. 0 //end
    23. };

    第一個(gè)section,,就是 //content 區(qū)域的整數(shù)值,這一塊區(qū)域在每一個(gè)QMetaObject的實(shí)體對(duì)象中數(shù)量都是相同的,,含義也相同,,但具體的值就不同了。專門有一個(gè)struct定義了這個(gè)section,,其含義在上面的注釋中已經(jīng)說(shuō)的很清楚了,。第二個(gè)section,,以 // signals 開頭的這段。這個(gè)section中的數(shù)值指明了QObject這個(gè)class包含了兩個(gè)signal,,第三個(gè)section,,以 // slots 開頭的這段。這個(gè)section中的數(shù)值指明了QObject這個(gè)class包含了兩個(gè)slot,。第四個(gè)section,,以 // properties 開頭的這段。這個(gè)section中的數(shù)值指明了QObject這個(gè)class包含有一個(gè)屬性定義,。第五個(gè)section,,以 // constructors 開頭的這段,指明了QObject這個(gè)class有兩個(gè)constructor,。

     (2)   virtual QObject::metaObject(); 這個(gè)的方法是返回一個(gè)該類對(duì)應(yīng)的staticMetaObject的const 指針,。也就是引用。

如果說(shuō)這個(gè)類沒(méi)有定義Q_OBJECT 宏定義,,那么這個(gè)類就沒(méi)有staticMetaObject這個(gè)靜態(tài)的QMetaObject對(duì)象,,那么這個(gè)時(shí)候這實(shí)例調(diào)用返回是是其父類的QMetaObject對(duì)象,因?yàn)檫@個(gè)虛函數(shù)沒(méi)有被重載,。所以一般建議都加上這個(gè)宏定義,。

      (3)qt_static_metacall()是一個(gè)靜態(tài)函數(shù),主要是用于對(duì)象的函數(shù)的調(diào)用的,。Qt 5.12版本或少以上,,這個(gè)靜態(tài)函數(shù)也會(huì)通過(guò)指針的方式存儲(chǔ)在QMetaObject 里面了。qt_static_metacall()提供了兩種的函數(shù)調(diào)用的方式,,一個(gè)是InvokeMetaMethod調(diào)用,,會(huì)將對(duì)象轉(zhuǎn)換為實(shí)際的對(duì)象類型,然后調(diào)用,。另外一個(gè)是IndexOfMethod調(diào)用,,會(huì)通過(guò)元函數(shù)的索引號(hào)調(diào)用。

 


Qt的slot和singnal

qt的slot和singnal的名稱都會(huì)存在的QMetaObject中,,但是這個(gè)時(shí)候,,slot和signal是沒(méi)有聯(lián)系的。需要進(jìn)行connect才可以使用,。

 例如:connect(ui->pushBtn,SIGNAL(clicked()),ui->lineEdit,SLOT(clear()));

 connect 在幕后到底都做了些什么事情,?為什么emit一個(gè)signal后,相應(yīng)的slot都會(huì)被調(diào)用,?

SIGNAL和SLOT宏定義

connect(&obj, SIGNAL(destroyed()), &app, SLOT(aboutQt()));

在這里signal和slot的名字都被包含在了兩個(gè)大寫的SIGNAL和SLOT中,,這兩個(gè)是什么呢?原來(lái)SIGNAL 和 SLOT 是Qt定義的兩個(gè)宏,。

# define SLOT(a)      ”1″#a  
# define SIGNAL(a)   ”2″#a  

Qt把signal和slot都轉(zhuǎn)化成了字符串,,并且還在這個(gè)字符串的前面加上了附加的符號(hào),,signal前面加了’2’,slot前面加了’1’,。也就是說(shuō),,我們前面寫了下面的connect調(diào)用,在經(jīng)過(guò)moc編譯器轉(zhuǎn)換之后,,就便成了:
connect(&obj, “2destroyed()”, &app, “1aboutQt()”)); 當(dāng)connect函數(shù)被調(diào)用了之后,,都會(huì)去檢查這兩個(gè)參數(shù)是否是使用這兩個(gè)宏正確的轉(zhuǎn)換而來(lái)的,它檢查的根據(jù)就是這兩個(gè)前置數(shù)字,,是否等于1或者是2,如果不是,,connect函數(shù)當(dāng)然就會(huì)失敗啦,!

 然后,會(huì)去檢查發(fā)送signal的對(duì)象是否有這個(gè)signal,,方法就是查找這個(gè)對(duì)象的class所對(duì)應(yīng)的staticMetaObject對(duì)象中所包含的d.stringdata所指向的字符串中是否包含這個(gè)signal的名字,,在這個(gè)檢查過(guò)程中,就會(huì)用到d.data所指向的那一串整數(shù),,通過(guò)這些整數(shù)值來(lái)計(jì)算每一個(gè)具體字符串的起始地址,。同理,還會(huì)使用同樣的方法去檢查slot,,看響應(yīng)這個(gè)signal的對(duì)象是否包含有相應(yīng)的slot,。這兩個(gè)檢查的任何一個(gè)如果失敗的話,connect函數(shù)就失敗了,,返回false,。

前面的步驟都是在做一些必要的檢查工作,下一步,,就是要把發(fā)送signal的對(duì)象和響應(yīng)signal的對(duì)象關(guān)聯(lián)起來(lái),。在QObject的私有數(shù)據(jù)類QObjectPrivate中,有下面這些數(shù)據(jù)結(jié)構(gòu)來(lái)保存這些信息:

  1. class QObjectPrivate : public QObjectData
  2. {
  3.   struct Connection
  4.   {
  5.     QObject *receiver;
  6.     int method;
  7.     uint connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking
  8.     QBasicAtomicPointer<int> argumentTypes;
  9.   };
  10.   typedef QList<Connection>; ConnectionList;
  11.   QObjectConnectionListVector *connectionLists;
  12.   struct Sender
  13.   {
  14.     QObject *sender;
  15.     int signal;
  16.     int ref;
  17.   };
  18.   QList<Sender> senders;
  19. };

    在發(fā)送signal的對(duì)象中,,每一個(gè)signal和slot的connection,,都會(huì)創(chuàng)建一個(gè)QObjectPrivate::Connection對(duì)象,并且把這個(gè)對(duì)象保存到connectionList這個(gè)Vector里面去,。

   在響應(yīng)signal的對(duì)象中,,同樣,也是每一個(gè)signal和slot的connection,,都會(huì)一個(gè)創(chuàng)建一個(gè)Sender對(duì)象,,并且把這個(gè)對(duì)象附加在Senders這個(gè)列表中。

 

emit幕后的故事

當(dāng)emit signal的時(shí)候,,與這個(gè)signal相連的slot函數(shù)就會(huì)被調(diào)用,,那么這個(gè)調(diào)用時(shí)如何發(fā)生的呢,?

看段代碼:

  1. class ZMytestObj : public QObject
  2. {
  3.   Q_OBJECT
  4.   signals:
  5.   void sigMenuClicked();
  6.   void sigBtnClicked();
  7. };
  1. void ZMytestObj::sigMenuClicked()
  2. {
  3.   QMetaObject::activate(this,&staticMetaObject,0,0);
  4. }
  5. void ZMytestObj::sigBtnClicked()
  6. {
  7.   QMetaObject::activate(this,&staticMetaObject,1,0);
  8. }

   每一個(gè)signal都會(huì)被轉(zhuǎn)換為一個(gè)與之相對(duì)應(yīng)的成員函數(shù)。也就是說(shuō),,當(dāng)我們寫下這樣一行代碼:emit sigBtnClicked();當(dāng)程序運(yùn)行到這里的時(shí)候,,實(shí)際上就是調(diào)用了void ZMytestObj::sigBtnClicked() 這個(gè)函數(shù)。

  void ZMytestObj::sigMenuClicked()  void ZMytestObj::sigBtnClicked(),,它們唯一的區(qū)別就是調(diào)用 QMetaObject::activate 函數(shù)時(shí)給出的參數(shù)不同,,一個(gè)是0,一個(gè)是1,,它們的含義是什么呢,?它們表示是這個(gè)類中的第幾個(gè)signal被發(fā)送出來(lái)了,回頭再去看頭文件就會(huì)發(fā)現(xiàn)它們就 是在這個(gè)類定義中,,signal定義出現(xiàn)的順序,,這個(gè)參數(shù)可是非常重要的,它直接決定了進(jìn)入這個(gè)函數(shù)體之后所發(fā)生的事情,。

當(dāng)執(zhí)行流程進(jìn)入到QMetaObject::activate函數(shù)中后,,會(huì)先從connectionLists這個(gè)變量中取出與這個(gè)signal相對(duì)應(yīng)的connection list,它根據(jù)的就是剛才所傳入進(jìn)來(lái)的signal index,。這個(gè)connection list中保存了所有和這個(gè)signal相鏈接的slot的信息,,每一對(duì)connection(即:signal和slot的連接)是這個(gè)list中的一項(xiàng)。

在每個(gè)一具體的鏈接記錄中,,還保存了這個(gè)鏈接的類型,,是自動(dòng)鏈接類型,還是隊(duì)列鏈接類型,,或者是阻塞鏈接類型,,不同的類型處理方法還不一樣的。

  1. if((c->connectionType == Qt::AutoConnection && !receiverInSameThread)
  2. || (c->connectionType == Qt::QueuedConnection)) {
  3. // 隊(duì)列處理
  4. } else if (c->connectionType == Qt::BlockingQueuedConnection) {
  5. // 阻塞處理
  6. // 如果同線程,,打印潛在死鎖,。
  7. } else {
  8. //直接調(diào)用槽函數(shù)或回調(diào)函數(shù)
  9. }

    如果信號(hào)-槽連接方式為QueuedConnection,不論是否在同一個(gè)線程,,按隊(duì)列處理,。如果信號(hào)-槽連接方式為Auto,且不在同一個(gè)線程,,也按隊(duì)列處理,。

  如果信號(hào)-槽連接方式為阻塞隊(duì)列BlockingQueuedConnection,按阻塞處理,。(注意同一個(gè)線程就不要按阻塞隊(duì)列調(diào)用了,。因?yàn)橥粋€(gè)線程,同時(shí)只能做一件事,,本身就是阻塞的,,直接調(diào)用就好了,,如果走阻塞隊(duì)列,則多了加鎖的過(guò)程,。如果槽中又發(fā)了同樣的信號(hào),,就會(huì)出現(xiàn)死鎖:加鎖之后還未解鎖,又來(lái)申請(qǐng)加鎖,。)

   隊(duì)列處理,,就是把槽函數(shù)的調(diào)用,轉(zhuǎn)化成了QMetaCallEvent事件,,通過(guò)QCoreApplication::postEvent

   放進(jìn)了事件循環(huán), 等到下一次事件分發(fā),,相應(yīng)的線程才會(huì)去調(diào)用槽函數(shù)。

    對(duì)于直接鏈接的類型,,先找到接收這個(gè)signal的對(duì)象的指針,,然后是處理這個(gè)signal的slot的index,已經(jīng)是否有需要處理的參數(shù),,然后就使用這些信息去調(diào)用receiver的qt_metcall 方法。在qt_metcall方法中就簡(jiǎn)單了,,根據(jù)slot的index,,一個(gè)大switch語(yǔ)句,調(diào)用相應(yīng)的slot函數(shù)就OK了,。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多