• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 319, comments - 22, trackbacks - 0, articles - 11
              C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

            詳解QT 信號機(jī)制 (上篇)轉(zhuǎn)載

            Posted on 2011-07-14 07:14 RTY 閱讀(900) 評論(0)  編輯 收藏 引用 所屬分類: Qt 、C/C++ 、轉(zhuǎn)載隨筆

            詳解QT 信號機(jī)制 (上篇)

            2011-07-05 18:32 佚名 互聯(lián)網(wǎng) 我要評論(1) 字號:T | T
            一鍵收藏,隨時查看,分享好友!

            信號不是Unix中進(jìn)程間通信的信號。這里的信號更多地與圖形界面的輸入輸出聯(lián)系在一起(當(dāng)然也可以是不可見的操作)。先來看內(nèi)容。

            AD:

            QT 信號機(jī)制 是本文要介紹的內(nèi)容,Qt用預(yù)編譯器和宏來保證強(qiáng)大的跨平臺能力,信號機(jī)制則是其中最精妙之處。本文分析了幾種常見的信號處理機(jī)制,然后詳細(xì)介紹了Qt的Signal/Slot機(jī)制。
             
            首先要說明,這里所說的信號不是Unix中進(jìn)程間通信的信號。這里的信號更多地與圖形界面的輸入輸出聯(lián)系在一起(當(dāng)然也可以是不可見的操作)。自從計算機(jī)程序從字符界面轉(zhuǎn)為圖形界面,用戶的輸入一下子變得繁雜和豐富起來,不同的輸入位置、不同的輸入設(shè)備、不同的焦點(diǎn)位置、不同的輸入值組合起來構(gòu)成了許許多多的信號。一下子,這個世界變得五彩繽紛。當(dāng)前的三大主流操作系統(tǒng)??Windows、Unix和MAC都提供了令人賞心悅目的圖形界面。

            雖然它們出自不同公司,自身還有很多分支,但是在圖形操作與管理上還是大致類同的:都有桌面、有圖標(biāo),有大大小小規(guī)則或不規(guī)則的窗口,窗口上有標(biāo)題、邊框、菜單以及按鈕等各種控件,用戶可以用鍵盤在當(dāng)前焦點(diǎn)輸入內(nèi)容,可以用鼠標(biāo)點(diǎn)擊任意的窗口和控件。就能動性來說,是由用戶主導(dǎo)程序下一步作何操作,而不象字符時代那樣由程序來主導(dǎo)用戶。這也就是所謂的“事件驅(qū)動”。在一個事件驅(qū)動的系統(tǒng)中,不論是Windows,還是Unix,都脫離不了以下的處理框架:

            當(dāng)某個應(yīng)用程序收到操作系統(tǒng)發(fā)送的事件時,它就要判斷這個事件該由誰處理。處理過程本身又可能引起新的事件發(fā)生,這就要告訴操作系統(tǒng)我發(fā)出了什么信號。如此這般循環(huán)往復(fù),青山之水常流。那么,一個具體的信號究竟是如何觸發(fā)與它對應(yīng)的函數(shù)呢?絕大部分的系統(tǒng)都是采用了回調(diào)的機(jī)制,所謂“回調(diào)”其實(shí)就是指向某一個函數(shù)的指針。

            在C語言中函數(shù)名其實(shí)也是一個指針,因此回調(diào)其實(shí)是一個指向指針的指針。在不同的開發(fā)框架或開發(fā)包中,對于回調(diào)的實(shí)現(xiàn)有著一些細(xì)微的差別。初接觸Qt時,我一直在想它是如何處理各種平臺的信號調(diào)用。雖然C語言本身是平臺無關(guān)的,但具體到某一個操作系統(tǒng)、某一個開發(fā)包,信號機(jī)制會有些不同。

            而信號是面向?qū)ο蟮拈_發(fā)環(huán)境中一個很重要的環(huán)節(jié),如果要設(shè)計一個類庫或程序框架,就必須很好地考慮不同平臺間的差異。接觸了Qt之后,感覺Qt選擇了一條頗具特色的處理途徑??Signals/Slot,中文名暫定為“信號/反應(yīng)槽”。在Qt的內(nèi)部設(shè)計中,通過信號/反應(yīng)槽(signals/slot)的使用對回調(diào)進(jìn)行了很好的封裝。為了更好地了解該機(jī)制我們先看一下其他幾種常用的信號相關(guān)程序。

            1.Win32

            Win32的程序總是從WinMain開始執(zhí)行。在WinMain的代碼中,主要功能一般有三個:一是注冊窗口類,二是在屏幕上顯示窗口,三是實(shí)現(xiàn)消息環(huán)。消息環(huán)的作用就是從應(yīng)用程序隊列中取出操作系統(tǒng)放入的消息,從而實(shí)現(xiàn)用戶和程序之間的交互(也包括象定時器之類的非用戶輸入的消息)。應(yīng)用程序不定期地在消息環(huán)中等待消息的到來。如下所示:// 消息環(huán)

            1. while(GetMessage(&msg, NULL, 0, 0))  
            2. {  
            3. TranslateMessage(&msg);  
            4. DispatchMessage(&msg);  

            這一段程序包括了形成一個標(biāo)準(zhǔn)消息環(huán)的三個基本API:GetM essage()、TranslateMessage()和ispatchMessage()

            ,采用加速鍵和非模式對話框時將相應(yīng)改變消息環(huán)的結(jié)構(gòu)。在Windows中,GetMessage()是多任務(wù)的核心。在應(yīng)用程序的消息隊列中出現(xiàn)一條消息之前,該函數(shù)并不返回任何東西。GetMessage()的等待阻塞了當(dāng)前進(jìn)程,因而為正在運(yùn)行的其他應(yīng)用程序提供了檢查私有消息環(huán)的機(jī)會。出現(xiàn)一條消息后,GetMessage()將取出該消息,并將信息存儲在一個MSG數(shù)據(jù)結(jié)構(gòu)中。對于每一條迫使退出消息環(huán)、進(jìn)程終止的消息(WM_QUIT除外),GetMessage()返回TRUE。

            通常在消息環(huán)后面跟一個返回語句,迫使WinMain()返回系統(tǒng)。緊跟著GetMessage()的TranslateMessage()對msg進(jìn)行處理并修改該數(shù)據(jù)塊的內(nèi)容。DispatchMessage()負(fù)責(zé)查找應(yīng)調(diào)用哪一個窗口過程,這種選擇是根據(jù)msg中hwnd所標(biāo)識的窗口進(jìn)行決策。窗口過程對消息進(jìn)行處理, 完畢后即返回到消息環(huán), 再次執(zhí)行GetMessage()。如下圖所示:

            為了對所關(guān)心的消息做出處理,窗口在創(chuàng)建時一定要提供一個消息回調(diào)函數(shù),不管該創(chuàng)建過程是顯式調(diào)用還是其他API函數(shù)隱式生成。用戶在該回調(diào)函數(shù)中要對每一個關(guān)心的消息做出判斷與處理,從C語言的觀點(diǎn)來看,一個窗口過程(回調(diào)函數(shù))就是這樣一個函數(shù):接受四個參數(shù),返回一個LRESULT值,一個switch語句在過程內(nèi)占用了大量的代碼以完成各個行為動作。

            2.MFC

            雖然直接用Win32 API開發(fā)的程序運(yùn)行效率高、條理分明,但開發(fā)起來卻較為復(fù)雜,維護(hù)工時也耗用較多,因此現(xiàn)在Windows環(huán)境中大部分用C++開發(fā)的應(yīng)用程序使用了微軟提供的MFC類庫。它是面向?qū)ο笤O(shè)計的,雖然乍一看其編程風(fēng)格與Win32迥然不同,但那是高度封裝的結(jié)果,其內(nèi)部的實(shí)現(xiàn)與Win32沒有區(qū)別。

            MFC的一個主導(dǎo)設(shè)計思想就是程序框架下(CFrameWnd)的視圖/文檔模型,同時定義了許多宏來簡化編程,其消息的傳遞也與宏息息相關(guān)(有關(guān)MFC的解剖可看侯捷先生的《深入淺出MFC》第二版)。通過使用這些宏,應(yīng)用程序自身將維護(hù)這一張可能為數(shù)不菲的消息映射表。對于程序員來說,只需要點(diǎn)擊鼠標(biāo)就可完成以上的工作,開發(fā)效率有了很大的提高。

            3.Linux

            Linux(包括其他的Unix)和Windows的一個很大不同點(diǎn)在于其圖形界面的管理是與內(nèi)核分開的,負(fù)責(zé)圖形操作(還包括鍵盤、鼠標(biāo)等事件捕獲)的模塊是X Window。請注意,此處的“Window”與微軟的Windows毫無親戚關(guān)系。X Window包括三大部分:服務(wù)端(XServer)、客戶端(X Client)和協(xié)議(X protocol),示意圖如下:我們平時在Linux下開發(fā)的有圖形界面的程序一般就是X Window中的客戶端程序,相對應(yīng)的庫就是X Lib。

            X Lib是X Window中最低層的接口庫,相當(dāng)于微軟Windows中的 API。這個庫封裝了對X protocol的存取,提供了超過610個函數(shù)。由于X protocol可以在網(wǎng)絡(luò)上傳播,因此X Window中服務(wù)器端和客戶端可以不在一臺機(jī)器上,這一點(diǎn)和微軟Windows有著很大的區(qū)別。對比X Lib與Win32 API的處理方式,可以發(fā)現(xiàn)雖然兩者框架不一、風(fēng)格不一,但在流程處理上都有異曲同工之妙。

            4.Qt

            Qt中的類庫有接近一半是從基類QObject上繼承下來,信號與反應(yīng)槽(signals/slot)機(jī)制就是用來在QObject類或其子類間通訊的方法。作為一種通用的處理機(jī)制,信號與反應(yīng)槽非常靈活,可以攜帶任意數(shù)量的參數(shù),參數(shù)的類型也由用戶自定。

            同時其本身也是類型安全的,任何一個從QObject或其子類繼承的用戶類都可以使用信號與反應(yīng)槽。信號的作用如同Windows系統(tǒng)中的消息。在Qt中,對于發(fā)出信號的對象來說,它并不知道是誰接收了這個信號。這樣的設(shè)計可能在某些地方會有些不便,但卻杜絕了緊耦合,于總體設(shè)計有利。反應(yīng)槽是用來接收信號的, 但它實(shí)際上也是普通的函數(shù),程序員可以象調(diào)用普通函數(shù)一樣來調(diào)用反應(yīng)槽。與信號類似的是,反應(yīng)槽的擁有者也不知道是誰向它發(fā)出了信號。

            在程序設(shè)計過程中,多個信號可以連接至一個反應(yīng)槽,類似的,一個信號也可以連接至多個反應(yīng)槽,甚至一個信號可以連接至另一個信號。在Windows中,如果我們需要多個菜單都激發(fā)一個函數(shù),一般是先寫一個共用函數(shù),然后在每個菜單的事件中調(diào)用此函數(shù)。在Qt中如果要實(shí)現(xiàn)同樣的功能,就可以把實(shí)現(xiàn)部分寫在一個菜單中,然后把其他菜單與這個菜單級聯(lián)起來。

            雖然信號/反應(yīng)槽機(jī)制有很多優(yōu)點(diǎn),使用也很方便,但它也不是沒有缺點(diǎn)。最大的缺點(diǎn)在于要稍微犧牲一點(diǎn)性能。根據(jù)Trolltech公司的自測,在CPU為Intel PentiumII 500 Mhz的PC機(jī)上,對于一個信號對應(yīng)一個反應(yīng)槽的連接來說,一秒鐘可以調(diào)用兩百萬次;對于一個信號對應(yīng)兩個反應(yīng)槽的連接來說,一秒鐘可以調(diào)用一百二十萬次。

            這個速度是不經(jīng)過連接而直接進(jìn)行回調(diào)的速度的十分之一。請注意這里的十分之一速度比是調(diào)用速度的比較,而不是一個完整函數(shù)執(zhí)行時間的比較。事實(shí)上一般情況下一個函數(shù)的總執(zhí)行時間大部分是在執(zhí)行部分,只有小部分是在調(diào)用部分,因些這個速度是可以接受的。這就象面向?qū)ο蟮木幊毯驮缧┠甑慕Y(jié)構(gòu)化編程相比一樣:程序的執(zhí)行效率并沒有提高,反而是有所下降的,但現(xiàn)在大家都在用面向?qū)ο蟮姆椒ň帉懗绦颉S靡徊糠謭?zhí)行效率換回開發(fā)效率與維護(hù)效率是值得的,況且現(xiàn)在已是P4為主流的時代。我們先來看一個簡單的樣例:

            1. class Demo : public QObject  
            2. {  
            3. Q_OBJECT  
            4. public:  
            5. Demo();  
            6. int value() const { return val; };  
            7. public slots:  
            8. void setValue( int );  
            9. signals:  
            10. void valueChanged( int );  
            11. private:  
            12. int val;  
            13. }; 

            由樣例可看到,類的定義中有兩個關(guān)鍵字slots和signals,還有一個宏Q_OBJECT。在Qt的程序中如果使用了信號與反應(yīng)槽就必須在類的定義中聲明這個宏,不過如果你聲明了該宏但在程序中并沒有信號與反應(yīng)槽,對程序也不會有任何影響,所以建議大家在用Qt寫程序時不妨都把這個宏加上。使用slots定義的就是信號的功能實(shí)現(xiàn),即反應(yīng)槽,例如:

            1. void Demo::setValue( int v )  
            2. {  
            3. if ( v != val ) {  
            4. vval = v;  
            5. emit valueChanged(v);  
            6. }  

            這段程序表明當(dāng)setValue執(zhí)行時它將釋放出valueChanged這個信號。以下程序示范了不同對象間信號與反應(yīng)槽的連接。

            1. Demo a, b;  
            2. connect(&a, SIGNAL(valueChanged(int)), &b, SLOT(setValue(int)));  
            3. b.setValue( 11 );  
            4. a.setValue( 7Array );  
            5. b.value(); // b的值將是7Array而不是原先設(shè)的11 

            在以上程序中,一旦信號與反應(yīng)槽連接,當(dāng)執(zhí)行a.setValue(7Array)時就會釋放出一個valueChanged(int)的信號,對象b將會收到這個信號并觸發(fā)setValue(int)這個函數(shù)。當(dāng)b在執(zhí)行setValue(int)這個函數(shù)時,它也將釋放valueChanged(int)這個信號,當(dāng)然b 的信號無人接收,因此就什么也沒干。示意圖如下:請注意,在樣例中我們僅當(dāng)輸入變量v不等于val時才釋放信號,因此就算對象

            a與b進(jìn)行了交叉連接也不會導(dǎo)致死循環(huán)的發(fā)生。由于在樣例中使用了Qt特有的關(guān)鍵字和宏,而Qt本身并不包括C++的編譯器,因此如果用流行的編譯程序(如Windows下的Visual C++或Linux下的gcc)是不能直接編譯這段代碼的,必須用Qt的中間編譯工具moc.exe把該段代碼轉(zhuǎn)換為無專用關(guān)鍵字和宏的C++代碼才能為這些編譯程序所解析、編譯與鏈接。

            以上代碼中信號與反應(yīng)槽的定義是在類中實(shí)現(xiàn)的。那么,非類成員的函數(shù),比如說一個全局函數(shù)可不可以也這樣做呢?答案是不行,只有是自身定義了信號的類或其子類才可以發(fā)出該種信號。一個對象的不同信號可以連接至不同的對象。

            當(dāng)一個信號被釋放時,與之連接的反應(yīng)槽將被立刻執(zhí)行,就象是在程序中直接調(diào)用該函數(shù)一樣。信號的釋放過程是阻塞的,這意味著只有當(dāng)反應(yīng)槽執(zhí)行完畢后該信號釋放過程才返回。如果一個信號與多個反應(yīng)槽連接,則這些反應(yīng)槽將被順序執(zhí)行,排序過程則是任意的。因此如果程序中對這些反應(yīng)槽的先后執(zhí)行次序有嚴(yán)格要求的話,應(yīng)特別注意。使用信號時還應(yīng)注意:信號的定義過程是在類的定義過程即頭文件中實(shí)現(xiàn)的

            為了中間編譯工具moc的正常運(yùn)行,不要在源文件(.cpp)中定義信號,同時信號本身不應(yīng)返回任何數(shù)據(jù)類型,即是空值(void)。如果你要設(shè)計一個通用的類或控件,則在信號或反應(yīng)槽的參數(shù)中應(yīng)盡可能使用常規(guī)數(shù)據(jù)以增加通用性。如上例代碼中valueChanged的參數(shù)為int型,如果它使用了特殊類型如QRangeControl::Range,那么這種信號只能與RangeControl中的反應(yīng)槽連接。如前所述,反應(yīng)槽也是常規(guī)函數(shù),與未定義slots的用戶函數(shù)在執(zhí)行上沒有任何區(qū)別。

            但在程序中不可把信號與常規(guī)函數(shù)連接在一起,否則信號的釋放不會引起對應(yīng)函數(shù)的執(zhí)行。要命的是中間編譯程序moc并不會對此種情況報錯,C++編譯程序更不會報錯。初學(xué)者比較容易忽略這一點(diǎn),往往是程序編好了沒有錯誤,邏輯上也正確,但運(yùn)行時就是不按自己的意愿出現(xiàn)結(jié)果,這時候應(yīng)檢查一下是不是這方面的疏忽。Qt的設(shè)計者之所以要這樣做估計是為了信號與反應(yīng)槽之間匹配的嚴(yán)格性。既然反應(yīng)槽與常規(guī)函數(shù)在執(zhí)行時沒有什么區(qū)別,因此它也可以定義成公共反應(yīng)槽(public slots)、保護(hù)反應(yīng)槽(protected slots)和私有反應(yīng)槽(private slots)。如果需要,我們也可以把反應(yīng)槽定義成虛函數(shù)以便子類進(jìn)行不同的實(shí)現(xiàn),這一點(diǎn)是非常有用的。

            只討論一下信號與反應(yīng)槽的使用好象還不過癮,既然Qt的X11 Free版提供了源代碼,我們就進(jìn)去看一下在QObject中connect的實(shí)現(xiàn)。由于Qt是一個跨平臺的開發(fā)庫,為了與不同平臺上的編譯器配合,它定義了一個中間類QMetaObject,該類的作用是存放有關(guān)信號/反應(yīng)槽以及對象自身的信息。這個類是Qt內(nèi)部使用的,用戶不應(yīng)去使用它。

            小結(jié):關(guān)于詳解QT 信號機(jī)制 (上篇)的內(nèi)容介紹完了,請繼續(xù)閱讀 詳解QT 信號機(jī)制 (下篇)。

            【編輯推薦】

            詳解QT 信號機(jī)制 (上篇)

            2011-07-05 18:32 佚名 互聯(lián)網(wǎng) 我要評論(1) 字號:T | T
            一鍵收藏,隨時查看,分享好友!

            信號不是Unix中進(jìn)程間通信的信號。這里的信號更多地與圖形界面的輸入輸出聯(lián)系在一起(當(dāng)然也可以是不可見的操作)。先來看內(nèi)容。

            AD:

            QT 信號機(jī)制 是本文要介紹的內(nèi)容,Qt用預(yù)編譯器和宏來保證強(qiáng)大的跨平臺能力,信號機(jī)制則是其中最精妙之處。本文分析了幾種常見的信號處理機(jī)制,然后詳細(xì)介紹了Qt的Signal/Slot機(jī)制。
             
            首先要說明,這里所說的信號不是Unix中進(jìn)程間通信的信號。這里的信號更多地與圖形界面的輸入輸出聯(lián)系在一起(當(dāng)然也可以是不可見的操作)。自從計算機(jī)程序從字符界面轉(zhuǎn)為圖形界面,用戶的輸入一下子變得繁雜和豐富起來,不同的輸入位置、不同的輸入設(shè)備、不同的焦點(diǎn)位置、不同的輸入值組合起來構(gòu)成了許許多多的信號。一下子,這個世界變得五彩繽紛。當(dāng)前的三大主流操作系統(tǒng)??Windows、Unix和MAC都提供了令人賞心悅目的圖形界面。

            雖然它們出自不同公司,自身還有很多分支,但是在圖形操作與管理上還是大致類同的:都有桌面、有圖標(biāo),有大大小小規(guī)則或不規(guī)則的窗口,窗口上有標(biāo)題、邊框、菜單以及按鈕等各種控件,用戶可以用鍵盤在當(dāng)前焦點(diǎn)輸入內(nèi)容,可以用鼠標(biāo)點(diǎn)擊任意的窗口和控件。就能動性來說,是由用戶主導(dǎo)程序下一步作何操作,而不象字符時代那樣由程序來主導(dǎo)用戶。這也就是所謂的“事件驅(qū)動”。在一個事件驅(qū)動的系統(tǒng)中,不論是Windows,還是Unix,都脫離不了以下的處理框架:

            當(dāng)某個應(yīng)用程序收到操作系統(tǒng)發(fā)送的事件時,它就要判斷這個事件該由誰處理。處理過程本身又可能引起新的事件發(fā)生,這就要告訴操作系統(tǒng)我發(fā)出了什么信號。如此這般循環(huán)往復(fù),青山之水常流。那么,一個具體的信號究竟是如何觸發(fā)與它對應(yīng)的函數(shù)呢?絕大部分的系統(tǒng)都是采用了回調(diào)的機(jī)制,所謂“回調(diào)”其實(shí)就是指向某一個函數(shù)的指針。

            在C語言中函數(shù)名其實(shí)也是一個指針,因此回調(diào)其實(shí)是一個指向指針的指針。在不同的開發(fā)框架或開發(fā)包中,對于回調(diào)的實(shí)現(xiàn)有著一些細(xì)微的差別。初接觸Qt時,我一直在想它是如何處理各種平臺的信號調(diào)用。雖然C語言本身是平臺無關(guān)的,但具體到某一個操作系統(tǒng)、某一個開發(fā)包,信號機(jī)制會有些不同。

            而信號是面向?qū)ο蟮拈_發(fā)環(huán)境中一個很重要的環(huán)節(jié),如果要設(shè)計一個類庫或程序框架,就必須很好地考慮不同平臺間的差異。接觸了Qt之后,感覺Qt選擇了一條頗具特色的處理途徑??Signals/Slot,中文名暫定為“信號/反應(yīng)槽”。在Qt的內(nèi)部設(shè)計中,通過信號/反應(yīng)槽(signals/slot)的使用對回調(diào)進(jìn)行了很好的封裝。為了更好地了解該機(jī)制我們先看一下其他幾種常用的信號相關(guān)程序。

            1.Win32

            Win32的程序總是從WinMain開始執(zhí)行。在WinMain的代碼中,主要功能一般有三個:一是注冊窗口類,二是在屏幕上顯示窗口,三是實(shí)現(xiàn)消息環(huán)。消息環(huán)的作用就是從應(yīng)用程序隊列中取出操作系統(tǒng)放入的消息,從而實(shí)現(xiàn)用戶和程序之間的交互(也包括象定時器之類的非用戶輸入的消息)。應(yīng)用程序不定期地在消息環(huán)中等待消息的到來。如下所示:// 消息環(huán)

            1. while(GetMessage(&msg, NULL, 0, 0))  
            2. {  
            3. TranslateMessage(&msg);  
            4. DispatchMessage(&msg);  

            這一段程序包括了形成一個標(biāo)準(zhǔn)消息環(huán)的三個基本API:GetM essage()、TranslateMessage()和ispatchMessage()

            ,采用加速鍵和非模式對話框時將相應(yīng)改變消息環(huán)的結(jié)構(gòu)。在Windows中,GetMessage()是多任務(wù)的核心。在應(yīng)用程序的消息隊列中出現(xiàn)一條消息之前,該函數(shù)并不返回任何東西。GetMessage()的等待阻塞了當(dāng)前進(jìn)程,因而為正在運(yùn)行的其他應(yīng)用程序提供了檢查私有消息環(huán)的機(jī)會。出現(xiàn)一條消息后,GetMessage()將取出該消息,并將信息存儲在一個MSG數(shù)據(jù)結(jié)構(gòu)中。對于每一條迫使退出消息環(huán)、進(jìn)程終止的消息(WM_QUIT除外),GetMessage()返回TRUE。

            通常在消息環(huán)后面跟一個返回語句,迫使WinMain()返回系統(tǒng)。緊跟著GetMessage()的TranslateMessage()對msg進(jìn)行處理并修改該數(shù)據(jù)塊的內(nèi)容。DispatchMessage()負(fù)責(zé)查找應(yīng)調(diào)用哪一個窗口過程,這種選擇是根據(jù)msg中hwnd所標(biāo)識的窗口進(jìn)行決策。窗口過程對消息進(jìn)行處理, 完畢后即返回到消息環(huán), 再次執(zhí)行GetMessage()。如下圖所示:

            為了對所關(guān)心的消息做出處理,窗口在創(chuàng)建時一定要提供一個消息回調(diào)函數(shù),不管該創(chuàng)建過程是顯式調(diào)用還是其他API函數(shù)隱式生成。用戶在該回調(diào)函數(shù)中要對每一個關(guān)心的消息做出判斷與處理,從C語言的觀點(diǎn)來看,一個窗口過程(回調(diào)函數(shù))就是這樣一個函數(shù):接受四個參數(shù),返回一個LRESULT值,一個switch語句在過程內(nèi)占用了大量的代碼以完成各個行為動作。

            2.MFC

            雖然直接用Win32 API開發(fā)的程序運(yùn)行效率高、條理分明,但開發(fā)起來卻較為復(fù)雜,維護(hù)工時也耗用較多,因此現(xiàn)在Windows環(huán)境中大部分用C++開發(fā)的應(yīng)用程序使用了微軟提供的MFC類庫。它是面向?qū)ο笤O(shè)計的,雖然乍一看其編程風(fēng)格與Win32迥然不同,但那是高度封裝的結(jié)果,其內(nèi)部的實(shí)現(xiàn)與Win32沒有區(qū)別。

            MFC的一個主導(dǎo)設(shè)計思想就是程序框架下(CFrameWnd)的視圖/文檔模型,同時定義了許多宏來簡化編程,其消息的傳遞也與宏息息相關(guān)(有關(guān)MFC的解剖可看侯捷先生的《深入淺出MFC》第二版)。通過使用這些宏,應(yīng)用程序自身將維護(hù)這一張可能為數(shù)不菲的消息映射表。對于程序員來說,只需要點(diǎn)擊鼠標(biāo)就可完成以上的工作,開發(fā)效率有了很大的提高。

            3.Linux

            Linux(包括其他的Unix)和Windows的一個很大不同點(diǎn)在于其圖形界面的管理是與內(nèi)核分開的,負(fù)責(zé)圖形操作(還包括鍵盤、鼠標(biāo)等事件捕獲)的模塊是X Window。請注意,此處的“Window”與微軟的Windows毫無親戚關(guān)系。X Window包括三大部分:服務(wù)端(XServer)、客戶端(X Client)和協(xié)議(X protocol),示意圖如下:我們平時在Linux下開發(fā)的有圖形界面的程序一般就是X Window中的客戶端程序,相對應(yīng)的庫就是X Lib。

            X Lib是X Window中最低層的接口庫,相當(dāng)于微軟Windows中的 API。這個庫封裝了對X protocol的存取,提供了超過610個函數(shù)。由于X protocol可以在網(wǎng)絡(luò)上傳播,因此X Window中服務(wù)器端和客戶端可以不在一臺機(jī)器上,這一點(diǎn)和微軟Windows有著很大的區(qū)別。對比X Lib與Win32 API的處理方式,可以發(fā)現(xiàn)雖然兩者框架不一、風(fēng)格不一,但在流程處理上都有異曲同工之妙。

            4.Qt

            Qt中的類庫有接近一半是從基類QObject上繼承下來,信號與反應(yīng)槽(signals/slot)機(jī)制就是用來在QObject類或其子類間通訊的方法。作為一種通用的處理機(jī)制,信號與反應(yīng)槽非常靈活,可以攜帶任意數(shù)量的參數(shù),參數(shù)的類型也由用戶自定。

            同時其本身也是類型安全的,任何一個從QObject或其子類繼承的用戶類都可以使用信號與反應(yīng)槽。信號的作用如同Windows系統(tǒng)中的消息。在Qt中,對于發(fā)出信號的對象來說,它并不知道是誰接收了這個信號。這樣的設(shè)計可能在某些地方會有些不便,但卻杜絕了緊耦合,于總體設(shè)計有利。反應(yīng)槽是用來接收信號的, 但它實(shí)際上也是普通的函數(shù),程序員可以象調(diào)用普通函數(shù)一樣來調(diào)用反應(yīng)槽。與信號類似的是,反應(yīng)槽的擁有者也不知道是誰向它發(fā)出了信號。

            在程序設(shè)計過程中,多個信號可以連接至一個反應(yīng)槽,類似的,一個信號也可以連接至多個反應(yīng)槽,甚至一個信號可以連接至另一個信號。在Windows中,如果我們需要多個菜單都激發(fā)一個函數(shù),一般是先寫一個共用函數(shù),然后在每個菜單的事件中調(diào)用此函數(shù)。在Qt中如果要實(shí)現(xiàn)同樣的功能,就可以把實(shí)現(xiàn)部分寫在一個菜單中,然后把其他菜單與這個菜單級聯(lián)起來。

            雖然信號/反應(yīng)槽機(jī)制有很多優(yōu)點(diǎn),使用也很方便,但它也不是沒有缺點(diǎn)。最大的缺點(diǎn)在于要稍微犧牲一點(diǎn)性能。根據(jù)Trolltech公司的自測,在CPU為Intel PentiumII 500 Mhz的PC機(jī)上,對于一個信號對應(yīng)一個反應(yīng)槽的連接來說,一秒鐘可以調(diào)用兩百萬次;對于一個信號對應(yīng)兩個反應(yīng)槽的連接來說,一秒鐘可以調(diào)用一百二十萬次。

            這個速度是不經(jīng)過連接而直接進(jìn)行回調(diào)的速度的十分之一。請注意這里的十分之一速度比是調(diào)用速度的比較,而不是一個完整函數(shù)執(zhí)行時間的比較。事實(shí)上一般情況下一個函數(shù)的總執(zhí)行時間大部分是在執(zhí)行部分,只有小部分是在調(diào)用部分,因些這個速度是可以接受的。這就象面向?qū)ο蟮木幊毯驮缧┠甑慕Y(jié)構(gòu)化編程相比一樣:程序的執(zhí)行效率并沒有提高,反而是有所下降的,但現(xiàn)在大家都在用面向?qū)ο蟮姆椒ň帉懗绦颉S靡徊糠謭?zhí)行效率換回開發(fā)效率與維護(hù)效率是值得的,況且現(xiàn)在已是P4為主流的時代。我們先來看一個簡單的樣例:

            1. class Demo : public QObject  
            2. {  
            3. Q_OBJECT  
            4. public:  
            5. Demo();  
            6. int value() const { return val; };  
            7. public slots:  
            8. void setValue( int );  
            9. signals:  
            10. void valueChanged( int );  
            11. private:  
            12. int val;  
            13. }; 

            由樣例可看到,類的定義中有兩個關(guān)鍵字slots和signals,還有一個宏Q_OBJECT。在Qt的程序中如果使用了信號與反應(yīng)槽就必須在類的定義中聲明這個宏,不過如果你聲明了該宏但在程序中并沒有信號與反應(yīng)槽,對程序也不會有任何影響,所以建議大家在用Qt寫程序時不妨都把這個宏加上。使用slots定義的就是信號的功能實(shí)現(xiàn),即反應(yīng)槽,例如:

            1. void Demo::setValue( int v )  
            2. {  
            3. if ( v != val ) {  
            4. vval = v;  
            5. emit valueChanged(v);  
            6. }  

            這段程序表明當(dāng)setValue執(zhí)行時它將釋放出valueChanged這個信號。以下程序示范了不同對象間信號與反應(yīng)槽的連接。

            1. Demo a, b;  
            2. connect(&a, SIGNAL(valueChanged(int)), &b, SLOT(setValue(int)));  
            3. b.setValue( 11 );  
            4. a.setValue( 7Array );  
            5. b.value(); // b的值將是7Array而不是原先設(shè)的11 

            在以上程序中,一旦信號與反應(yīng)槽連接,當(dāng)執(zhí)行a.setValue(7Array)時就會釋放出一個valueChanged(int)的信號,對象b將會收到這個信號并觸發(fā)setValue(int)這個函數(shù)。當(dāng)b在執(zhí)行setValue(int)這個函數(shù)時,它也將釋放valueChanged(int)這個信號,當(dāng)然b 的信號無人接收,因此就什么也沒干。示意圖如下:請注意,在樣例中我們僅當(dāng)輸入變量v不等于val時才釋放信號,因此就算對象

            a與b進(jìn)行了交叉連接也不會導(dǎo)致死循環(huán)的發(fā)生。由于在樣例中使用了Qt特有的關(guān)鍵字和宏,而Qt本身并不包括C++的編譯器,因此如果用流行的編譯程序(如Windows下的Visual C++或Linux下的gcc)是不能直接編譯這段代碼的,必須用Qt的中間編譯工具moc.exe把該段代碼轉(zhuǎn)換為無專用關(guān)鍵字和宏的C++代碼才能為這些編譯程序所解析、編譯與鏈接。

            以上代碼中信號與反應(yīng)槽的定義是在類中實(shí)現(xiàn)的。那么,非類成員的函數(shù),比如說一個全局函數(shù)可不可以也這樣做呢?答案是不行,只有是自身定義了信號的類或其子類才可以發(fā)出該種信號。一個對象的不同信號可以連接至不同的對象。

            當(dāng)一個信號被釋放時,與之連接的反應(yīng)槽將被立刻執(zhí)行,就象是在程序中直接調(diào)用該函數(shù)一樣。信號的釋放過程是阻塞的,這意味著只有當(dāng)反應(yīng)槽執(zhí)行完畢后該信號釋放過程才返回。如果一個信號與多個反應(yīng)槽連接,則這些反應(yīng)槽將被順序執(zhí)行,排序過程則是任意的。因此如果程序中對這些反應(yīng)槽的先后執(zhí)行次序有嚴(yán)格要求的話,應(yīng)特別注意。使用信號時還應(yīng)注意:信號的定義過程是在類的定義過程即頭文件中實(shí)現(xiàn)的。

            為了中間編譯工具moc的正常運(yùn)行,不要在源文件(.cpp)中定義信號,同時信號本身不應(yīng)返回任何數(shù)據(jù)類型,即是空值(void)。如果你要設(shè)計一個通用的類或控件,則在信號或反應(yīng)槽的參數(shù)中應(yīng)盡可能使用常規(guī)數(shù)據(jù)以增加通用性。如上例代碼中valueChanged的參數(shù)為int型,如果它使用了特殊類型如QRangeControl::Range,那么這種信號只能與RangeControl中的反應(yīng)槽連接。如前所述,反應(yīng)槽也是常規(guī)函數(shù),與未定義slots的用戶函數(shù)在執(zhí)行上沒有任何區(qū)別。

            但在程序中不可把信號與常規(guī)函數(shù)連接在一起,否則信號的釋放不會引起對應(yīng)函數(shù)的執(zhí)行。要命的是中間編譯程序moc并不會對此種情況報錯,C++編譯程序更不會報錯。初學(xué)者比較容易忽略這一點(diǎn),往往是程序編好了沒有錯誤,邏輯上也正確,但運(yùn)行時就是不按自己的意愿出現(xiàn)結(jié)果,這時候應(yīng)檢查一下是不是這方面的疏忽。Qt的設(shè)計者之所以要這樣做估計是為了信號與反應(yīng)槽之間匹配的嚴(yán)格性。既然反應(yīng)槽與常規(guī)函數(shù)在執(zhí)行時沒有什么區(qū)別,因此它也可以定義成公共反應(yīng)槽(public slots)、保護(hù)反應(yīng)槽(protected slots)和私有反應(yīng)槽(private slots)。如果需要,我們也可以把反應(yīng)槽定義成虛函數(shù)以便子類進(jìn)行不同的實(shí)現(xiàn),這一點(diǎn)是非常有用的。

            只討論一下信號與反應(yīng)槽的使用好象還不過癮,既然Qt的X11 Free版提供了源代碼,我們就進(jìn)去看一下在QObject中connect的實(shí)現(xiàn)。由于Qt是一個跨平臺的開發(fā)庫,為了與不同平臺上的編譯器配合,它定義了一個中間類QMetaObject,該類的作用是存放有關(guān)信號/反應(yīng)槽以及對象自身的信息。這個類是Qt內(nèi)部使用的,用戶不應(yīng)去使用它。

            小結(jié):關(guān)于詳解QT 信號機(jī)制 (上篇)的內(nèi)容介紹完了,請繼續(xù)閱讀 詳解QT 信號機(jī)制 (下篇)。

            【編輯推薦】

            久久亚洲AV无码精品色午夜| 青青草原综合久久| 国产精品无码久久综合网| 久久99久久99小草精品免视看| 青草国产精品久久久久久| 成人久久精品一区二区三区| 久久www免费人成看国产片| 亚洲人成网站999久久久综合| 色欲综合久久中文字幕网| 青青青青久久精品国产h| 久久久久久国产精品无码下载| 99久久99这里只有免费费精品 | 99久久精品国产综合一区| 久久精品国产亚洲5555| 久久婷婷五月综合色奶水99啪| 久久久精品视频免费观看| 久久国产精品无码一区二区三区| 久久久久成人精品无码 | 国内精品久久久久久不卡影院| 日韩AV毛片精品久久久| 久久国产色AV免费看| 欧美亚洲国产精品久久| 国产精品美女久久久久AV福利 | 国产免费福利体检区久久| 无码人妻久久一区二区三区免费丨 | 国产三级精品久久| 久久精品毛片免费观看| 久久九九兔免费精品6| 久久国产成人| 嫩草影院久久99| 狠色狠色狠狠色综合久久| 久久久无码人妻精品无码| 一本色道久久HEZYO无码| 国产精品中文久久久久久久| 亚洲?V乱码久久精品蜜桃| 久久九色综合九色99伊人| 精品久久国产一区二区三区香蕉| 国产精品久久免费| 久久青青草原综合伊人| 中文字幕亚洲综合久久| 国产精品gz久久久|