<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # 第?4?章?事件處理 ### 目錄 * [4.1 概述](eventhandling.html#eventhandling_general) * [4.2 信號 Signals](eventhandling.html#eventhandling_signals) * [4.3 連接 Connections](eventhandling.html#eventhandling_connections) * [4.4 練習](eventhandling.html#eventhandling_exercises) [![](https://box.kancloud.cn/2016-02-29_56d41c2d6e214.gif)](http://creativecommons.org/licenses/by-nc-nd/3.0/de/deed.zh) 該書采用 [Creative Commons License](http://creativecommons.org/licenses/by-nc-nd/3.0/de/deed.zh) 授權 ## 4.1.?概述 很多開發者在聽到術語'事件處理'時就會想到GUI:點擊一下某個按鈕,相關聯的功能就會被執行。 點擊本身就是事件,而功能就是相對應的事件處理器。 這一模式的使用當然不僅限于GUI。 一般情況下,任意對象都可以調用基于特定事件的專門函數。 本章所介紹的 [Boost.Signals](http://www.boost.org/libs/signals) 庫提供了一個簡單的方法在 C++ 中應用這一模式。 嚴格來說,Boost.Function 庫也可以用于事件處理。 不過,Boost.Function 和 Boost.Signals 之間的一個主要區別在于,Boost.Signals 能夠將一個以上的事件處理器關聯至單個事件。 因此,Boost.Signals 可以更好地支持事件驅動的開發,當需要進行事件處理時,應作為第一選擇。 ## 4.2.?信號 Signals 雖然這個庫的名字乍一看好象有點誤導,但實際上并非如此。 Boost.Signals 所實現的模式被命名為 '信號至插槽' (signal to slot),它基于以下概念:當對應的信號被發出時,相關聯的插槽即被執行。 原則上,你可以把單詞 '信號' 和 '插槽' 分別替換為 '事件' 和 '事件處理器'。 不過,由于信號可以在任意給定的時間發出,所以這一概念放棄了 '事件' 的名字。 因此,Boost.Signals 沒有提供任何類似于 '事件' 的類。 相反,它提供了一個名為 `boost::signal` 的類,定義于 `boost/signal.hpp`. 實際上,這個頭文件是唯一一個需要知道的,因為它會自動包含其它相關的頭文件。 Boost.Signals 定義了其它一些類,位于 boost::signals 名字空間中。 由于 `boost::signal` 是最常被用到的類,所以它是位于名字空間 boost 中的。 ``` #include <boost/signal.hpp> #include <iostream> void func() { std::cout << "Hello, world!" << std::endl; } int main() { boost::signal<void ()> s; s.connect(func); s(); } ``` * [下載源代碼](src/4.2.1/main.cpp) `boost::signal` 實際上被實現為一個模板函數,具有被用作為事件處理器的函數的簽名,該簽名也是它的模板參數。 在這個例子中,只有簽名為 `void ()` 的函數可以被成功關聯至信號 `s`。 函數 `func()` 被通過 `connect()` 方法關聯至信號 `s`。 由于 `func()` 符合所要求的 `void ()` 簽名,所以該關聯成功建立。因此當信號 `s` 被觸發時,`func()` 將被調用。 信號是通過調用 `s` 來觸發的,就象普通的函數調用那樣。 這個函數的簽名對應于作為模板參數傳入的簽名:因為 `void ()` 不要求任何參數,所以括號內是空的。 調用 `s` 會引發一個觸發器,進而執行相應的 `func()` 函數 - 之前用 `connect()` 關聯了的。 同一例子也可以用 Boost.Function 來實現。 ``` #include <boost/function.hpp> #include <iostream> void func() { std::cout << "Hello, world!" << std::endl; } int main() { boost::function<void ()> f; f = func; f(); } ``` * [下載源代碼](src/4.2.2/main.cpp) 和前一個例子相類似,`func()` 被關聯至 `f`。 當 `f` 被調用時,就會相應地執行 `func()`。 Boost.Function 僅限于這種情形下適用,而 Boost.Signals 則提供了多得多的方式,如關聯多個函數至單個特定信號,示例如下。 ``` #include <boost/signal.hpp> #include <iostream> void func1() { std::cout << "Hello" << std::flush; } void func2() { std::cout << ", world!" << std::endl; } int main() { boost::signal<void ()> s; s.connect(func1); s.connect(func2); s(); } ``` * [下載源代碼](src/4.2.3/main.cpp) `boost::signal` 可以通過反復調用 `connect()` 方法來把多個函數賦值給單個特定信號。 當該信號被觸發時,這些函數被按照之前用 `connect()` 進行關聯時的順序來執行。 另外,執行的順序也可通過 `connect()` 方法的另一個重載版本來明確指定,該重載版本要求以一個 `int` 類型的值作為額外的參數。 ``` #include <boost/signal.hpp> #include <iostream> void func1() { std::cout << "Hello" << std::flush; } void func2() { std::cout << ", world!" << std::endl; } int main() { boost::signal<void ()> s; s.connect(1, func2); s.connect(0, func1); s(); } ``` * [下載源代碼](src/4.2.4/main.cpp) 和前一個例子一樣,`func1()` 在 `func2()` 之前執行。 要釋放某個函數與給定信號的關聯,可以用 `disconnect()` 方法。 ``` #include <boost/signal.hpp> #include <iostream> void func1() { std::cout << "Hello" << std::endl; } void func2() { std::cout << ", world!" << std::endl; } int main() { boost::signal<void ()> s; s.connect(func1); s.connect(func2); s.disconnect(func2); s(); } ``` * [下載源代碼](src/4.2.5/main.cpp) 這個例子僅輸出 `Hello`,因為與 `func2()` 的關聯在觸發信號之前已經被釋放。 除了 `connect()` 和 `disconnect()` 以外,`boost::signal` 還提供了幾個方法。 ``` #include <boost/signal.hpp> #include <iostream> void func1() { std::cout << "Hello" << std::flush; } void func2() { std::cout << ", world!" << std::endl; } int main() { boost::signal<void ()> s; s.connect(func1); s.connect(func2); std::cout << s.num_slots() << std::endl; if (!s.empty()) s(); s.disconnect_all_slots(); } ``` * [下載源代碼](src/4.2.6/main.cpp) `num_slots()` 返回已關聯函數的數量。如果沒有函數被關聯,則 `num_slots()` 返回0。 在這種特定情況下,可以用 `empty()` 方法來替代。 `disconnect_all_slots()` 方法所做的實際上正是它的名字所表達的:釋放所有已有的關聯。 看完了函數如何被關聯至信號,以及弄明白了信號被觸發時會發生什么事之后,還有一個問題:這些函數的返回值去了哪里? 以下例子回答了這個問題。 ``` #include <boost/signal.hpp> #include <iostream> int func1() { return 1; } int func2() { return 2; } int main() { boost::signal<int ()> s; s.connect(func1); s.connect(func2); std::cout << s() << std::endl; } ``` * [下載源代碼](src/4.2.7/main.cpp) `func1()` 和 `func2()` 都具有 `int` 類型的返回值。 `s` 將處理兩個返回值,并將它們都寫出至標準輸出流。 那么,到底會發生什么呢? 以上例子實際上會把 `2` 寫出至標準輸出流。 兩個返回值都被 `s` 正確接收,但除了最后一個值,其它值都會被忽略。 缺省情況下,所有被關聯函數中,實際上只有最后一個返回值被返回。 你可以定制一個信號,令每個返回值都被相應地處理。 為此,要把一個稱為合成器(combiner)的東西作為第二個參數傳遞給 `boost::signal`。 ``` #include <boost/signal.hpp> #include <iostream> #include <algorithm> int func1() { return 1; } int func2() { return 2; } template <typename T> struct min_element { typedef T result_type; template <typename InputIterator> T operator()(InputIterator first, InputIterator last) const { return *std::min_element(first, last); } }; int main() { boost::signal<int (), min_element<int> > s; s.connect(func1); s.connect(func2); std::cout << s() << std::endl; } ``` * [下載源代碼](src/4.2.8/main.cpp) 合成器是一個重載了 `operator()()` 操作符的類。這個操作符會被自動調用,傳入兩個迭代器,指向某個特定信號的所有返回值。 以上例子使用了標準 C++ 算法 `std::min_element()` 來確定并返回最小的值。 不幸的是,我們不可能把象 `std::min_element()` 這樣的一個算法直接傳給 `boost::signal` 作為一個模板參數。 `boost::signal` 要求這個合成器定義一個名為 `result_type` 的類型,用于說明 `operator()()` 操作符返回值的類型。 由于在標準 C++ 算法中缺少這個類型,所以在編譯時會產生一個相應的錯誤。 除了對返回值進行分析以外,合成器也可以保存它們。 ``` #include <boost/signal.hpp> #include <iostream> #include <vector> #include <algorithm> int func1() { return 1; } int func2() { return 2; } template <typename T> struct min_element { typedef T result_type; template <typename InputIterator> T operator()(InputIterator first, InputIterator last) const { return T(first, last); } }; int main() { boost::signal<int (), min_element<std::vector<int> > > s; s.connect(func1); s.connect(func2); std::vector<int> v = s(); std::cout << *std::min_element(v.begin(), v.end()) << std::endl; } ``` * [下載源代碼](src/4.2.9/main.cpp) 這個例子把所有返回值保存在一個 vector 中,再由 `s()` 返回。 ## 4.3.?連接 Connections 函數可以通過由 `boost::signal` 所提供的 `connect()` 和 `disconnect()` 方法的幫助來進行管理。 由于 `connect()` 會返回一個類型為 `boost::signals::connection` 的值,它們可以通過其它方法來管理。 ``` #include <boost/signal.hpp> #include <iostream> void func() { std::cout << "Hello, world!" << std::endl; } int main() { boost::signal<void ()> s; boost::signals::connection c = s.connect(func); s(); c.disconnect(); } ``` * [下載源代碼](src/4.3.1/main.cpp) `boost::signal` 的 `disconnect()` 方法需要傳入一個函數指針,而直接調用 `boost::signals::connection` 對象上的 `disconnect()` 方法則略去該參數。 除了 `disconnect()` 方法之外,`boost::signals::connection` 還提供了其它方法,如 `block()` 和 `unblock()`。 ``` #include <boost/signal.hpp> #include <iostream> void func() { std::cout << "Hello, world!" << std::endl; } int main() { boost::signal<void ()> s; boost::signals::connection c = s.connect(func); c.block(); s(); c.unblock(); s(); } ``` * [下載源代碼](src/4.3.2/main.cpp) 以上程序只會執行一次 `func()`。 雖然信號 `s` 被觸發了兩次,但是在第一次觸發時 `func()` 不會被調用,因為連接 `c` 實際上已經被 `block()` 調用所阻塞。 由于在第二次觸發之前調用了 `unblock()`,所以之后 `func()` 被正確地執行。 除了 `boost::signals::connection` 以外,還有一個名為 `boost::signals::scoped_connection` 的類,它會在析構時自動釋放連接。 ``` #include <boost/signal.hpp> #include <iostream> void func() { std::cout << "Hello, world!" << std::endl; } int main() { boost::signal<void ()> s; { boost::signals::scoped_connection c = s.connect(func); } s(); } ``` * [下載源代碼](src/4.3.3/main.cpp) 因為連接對象 `c` 在信號觸發之前被銷毀,所以 `func()` 不會被調用。 `boost::signals::scoped_connection` 實際上是派生自 `boost::signals::connection` 的,所以它提供了相同的方法。它們之間的區別僅在于,在析構 `boost::signals::scoped_connection` 時,連接會自動釋放。 雖然 `boost::signals::scoped_connection` 的確令自動釋放連接更為容易,但是該類型的對象仍需要管理。 如果在其它情形下連接也可以被自動釋放,而且不需要管理這些對象的話,就更好了。 ``` #include <boost/signal.hpp> #include <boost/bind.hpp> #include <iostream> #include <memory> class world { public: void hello() const { std::cout << "Hello, world!" << std::endl; } }; int main() { boost::signal<void ()> s; { std::auto_ptr<world> w(new world()); s.connect(boost::bind(&world::hello, w.get())); } std::cout << s.num_slots() << std::endl; s(); } ``` * [下載源代碼](src/4.3.4/main.cpp) 以上程序使用 Boost.Bind 將一個對象的方法關聯至一個信號。 在信號觸發之前,這個對象就被銷毀了,這會產生問題。 我們不傳遞實際的對象 `w`,而只傳遞一個指針給 `boost::bind()`。 在 `s()` 被實際調用的時候,該指針所引向的對象已不再存在。 可以如下修改這個程序,使得一旦對象 `w` 被銷毀,連接就會自動釋放。 ``` #include <boost/signal.hpp> #include <boost/bind.hpp> #include <iostream> #include <memory> class world : public boost::signals::trackable { public: void hello() const { std::cout << "Hello, world!" << std::endl; } }; int main() { boost::signal<void ()> s; { std::auto_ptr<world> w(new world()); s.connect(boost::bind(&world::hello, w.get())); } std::cout << s.num_slots() << std::endl; s(); } ``` * [下載源代碼](src/4.3.5/main.cpp) 如果現在再執行,`num_slots()` 會返回 `0` 以確保不會試圖調用已銷毀對象之上的方法。 僅需的修改是讓 `world` 類繼承自 `boost::signals::trackable`。 當使用對象的指針而不是對象的副本來關聯函數至信號時,`boost::signals::trackable` 可以顯著簡化連接的管理。 ## 4.4.?練習 You can buy [solutions to all exercises](http://en.highscore.de/shop/index.php?p=boost-solution) in this book as a ZIP file. 1. 編寫一個程序,定義一個名為 `button` 的類,表示GUI中的一個可點擊按鈕。 為該類加入兩個方法 `add_handler()` 和 `remove_handler()`,它們均要求一個函數名作為參數。 如果 `click()` 方法被調用,已登記的函數將被按順序執行。 如下測試你的代碼,創建一個 `button` 類的實例,從事件處理器內部向標準輸出流寫出一個信息。 調用 `click()` 函數模擬用鼠標點擊該按鈕。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看