<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>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # 第?3?章?函數對象 ### 目錄 * [3.1 概述](functionobjects.html#functionobjects_general) * [3.2 Boost.Bind](functionobjects.html#functionobjects_bind) * [3.3 Boost.Ref](functionobjects.html#functionobjects_ref) * [3.4 Boost.Function](functionobjects.html#functionobjects_function) * [3.5 Boost.Lambda](functionobjects.html#functionobjects_lambda) * [3.6 練習](functionobjects.html#functionobjects_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) 授權 ## 3.1.?概述 本章介紹的是函數對象,可能稱為'高階函數'更為適合。 它實際上是指那些可以被傳入到其它函數或是從其它函數返回的一類函數。 在C++中高階函數是被實現為函數對象的,所以這個標題還是有意義的。 在這整一章中,將會介紹幾個用于處理函數對象的 Boost C++ 庫。 其中,[Boost.Bind](http://www.boost.org/libs/bind/) 可替換來自C++標準的著名的 `std::bind1st()` 和 `std::bind2nd()` 函數,而 [Boost.Function](http://www.boost.org/libs/function/) 則提供了一個用于封裝函數指針的類。 最后,[Boost.Lambda](http://www.boost.org/libs/lambda/) 則引入了一種創建匿名函數的方法。 ## 3.2.?Boost.Bind Boost.Bind 是這樣的一個庫,它簡化了由C++標準中的 `std::bind1st()` 和 `std::bind2nd()` 模板函數所提供的一個機制:將這些函數與幾乎不限數量的參數一起使用,就可以得到指定簽名的函數。 這種情形的一個最好的例子就是在C++標準中定義的多個不同算法。 ``` #include <iostream> #include <vector> #include <algorithm> void print(int i) { std::cout << i << std::endl; } int main() { std::vector<int> v; v.push_back(1); v.push_back(3); v.push_back(2); std::for_each(v.begin(), v.end(), print); } ``` * [下載源代碼](src/3.2.1/main.cpp) 算法 `std::for_each()` 要求它的第三個參數是一個僅接受正好一個參數的函數或函數對象。 如果 `std::for_each()` 被執行,指定容器中的所有元素 - 在上例中,這些元素的類型為 `int` - 將按順序被傳入至 `print()` 函數。 但是,如果要使用一個具有不同簽名的函數的話,事情就復雜了。 例如,如果要傳入的是以下函數 `add()`,它要將一個常數值加至容器中的每個元素上,并顯示結果。 ``` void add(int i, int j) { std::cout << i + j << std::endl; } ``` 由于 `std::for_each()` 要求的是僅接受一個參數的函數,所以不能直接傳入 `add()` 函數。 源代碼必須要修改。 ``` #include <iostream> #include <vector> #include <algorithm> #include <functional> class add : public std::binary_function<int, int, void> { public: void operator()(int i, int j) const { std::cout << i + j << std::endl; } }; int main() { std::vector<int> v; v.push_back(1); v.push_back(3); v.push_back(2); std::for_each(v.begin(), v.end(), std::bind1st(add(), 10)); } ``` * [下載源代碼](src/3.2.2/main.cpp) 以上程序將值10加至容器 `v` 的每個元素之上,并使用標準輸出流顯示結果。 源代碼必須作出大幅的修改,以實現此功能:`add()` 函數已被轉換為一個派生自 `std::binary_function` 的函數對象。 Boost.Bind 簡化了不同函數之間的綁定。 它只包含一個 `boost::bind()` 模板函數,定義于 `boost/bind.hpp` 中。 使用這個函數,可以如下實現以上例子: ``` #include <boost/bind.hpp> #include <iostream> #include <vector> #include <algorithm> void add(int i, int j) { std::cout << i + j << std::endl; } int main() { std::vector<int> v; v.push_back(1); v.push_back(3); v.push_back(2); std::for_each(v.begin(), v.end(), boost::bind(add, 10, _1)); } ``` * [下載源代碼](src/3.2.3/main.cpp) 象 `add()` 這樣的函數不再需要為了要用于 `std::for_each()` 而轉換為函數對象。 使用 `boost::bind()`,這個函數可以忽略其第一個參數而使用。 因為 `add()` 函數要求兩個參數,兩個參數都必須傳遞給 `boost::bind()`。 第一個參數是常數值10,而第二個參數則是一個怪異的 `_1`。 `_1` 被稱為占位符(placeholder),定義于 Boost.Bind。 除了 `_1`,Boost.Bind 還定義了 `_2` 和 `_3`。 通過使用這些占位符,`boost::bind()` 可以變為一元、二元或三元的函數。 對于 `_1`, `boost::bind()` 變成了一個一元函數 - 即只要求一個參數的函數。 這是必需的,因為 `std::for_each()` 正是要求一個一元函數作為其第三個參數。 當這個程序執行時,`std::for_each()` 對容器 `v` 中的第一個元素調用該一元函數。 元素的值通過占位符 `_1` 傳入到一元函數中。 這個占位符和常數值被進一步傳遞到 `add()` 函數。 通過使用這種機制,`std::for_each()` 只看到了由 `boost::bind()` 所定義的一元函數。 而 `boost::bind()` 本身則只是調用了另一個函數,并將常數值或占位符作為參數傳入給它。 下面這個例子通過 `boost::bind()` 定義了一個二元函數,用于 `std::sort()` 算法,該算法要求一個二元函數作為其第三個參數。 ``` #include <boost/bind.hpp> #include <vector> #include <algorithm> bool compare(int i, int j) { return i > j; } int main() { std::vector<int> v; v.push_back(1); v.push_back(3); v.push_back(2); std::sort(v.begin(), v.end(), boost::bind(compare, _1, _2)); } ``` * [下載源代碼](src/3.2.4/main.cpp) 因為使用了兩個占位符 `_1` 和 `_2`,所以 `boost::bind()` 定義了一個二元函數。 `std::sort()` 算法以容器 `v` 的兩個元素來調用該函數,并根據返回值來對容器進行排序。 基于 `compare()` 函數的定義,容器將被按降序排列。 但是,由于 `compare()` 本身就是一個二元函數,所以使用 `boost::bind()` 確是多余的。 ``` #include <boost/bind.hpp> #include <vector> #include <algorithm> bool compare(int i, int j) { return i > j; } int main() { std::vector<int> v; v.push_back(1); v.push_back(3); v.push_back(2); std::sort(v.begin(), v.end(), compare); } ``` * [下載源代碼](src/3.2.5/main.cpp) 不過使用 `boost::bind()` 還是有意義的。例如,如果容器要按升序排列而又不能修改 `compare()` 函數的定義。 ``` #include <boost/bind.hpp> #include <vector> #include <algorithm> bool compare(int i, int j) { return i > j; } int main() { std::vector<int> v; v.push_back(1); v.push_back(3); v.push_back(2); std::sort(v.begin(), v.end(), boost::bind(compare, _2, _1)); } ``` * [下載源代碼](src/3.2.6/main.cpp) 該例子僅改變了占位符的順序:`_2` 被作為第一參數傳遞,而 `_1` 則被作為第二參數傳遞至 `compare()`,這樣即可改變排序的順序。 ## 3.3.?Boost.Ref 本庫 [Boost.Ref](http://www.boost.org/doc/html/ref.html) 通常與 Boost.Bind 一起使用,所以我把它們挨著寫。 它提供了兩個函數 - `boost::ref()` 和 `boost::cref()` - 定義于 `boost/ref.hpp`. 當要用于 `boost::bind()` 的函數帶有至少一個引用參數時,Boost.Ref 就很重要了。 由于 `boost::bind()` 會復制它的參數,所以引用必須特別處理。 ``` #include <boost/bind.hpp> #include <iostream> #include <vector> #include <algorithm> void add(int i, int j, std::ostream &os) { os << i + j << std::endl; } int main() { std::vector<int> v; v.push_back(1); v.push_back(3); v.push_back(2); std::for_each(v.begin(), v.end(), boost::bind(add, 10, _1, boost::ref(std::cout))); } ``` * [下載源代碼](src/3.3.1/main.cpp) 以上例子使用了上一節中的 `add()` 函數。 不過這一次該函數需要一個流對象的引用來打印信息。 因為傳給 `boost::bind()` 的參數是以值方式傳遞的,所以 `std::cout` 不能直接使用,否則該函數會試圖創建它的一份拷貝。 通過使用模板函數 `boost::ref()`,象 `std::cout` 這樣的流就可以被以引用方式傳遞,也就可以成功編譯上面這個例子了。 要以引用方式傳遞常量對象,可以使用模板函數 `boost::cref()`。 ## 3.4.?Boost.Function 為了封裝函數指針,[Boost.Function](http://www.boost.org/libs/function/) 提供了一個名為 `boost::function` 的類。 它定義于 `boost/function.hpp`,用法如下: ``` #include <boost/function.hpp> #include <iostream> #include <cstdlib> #include <cstring> int main() { boost::function<int (const char*)> f = std::atoi; std::cout << f("1609") << std::endl; f = std::strlen; std::cout << f("1609") << std::endl; } ``` * [下載源代碼](src/3.4.1/main.cpp) `boost::function` 可以定義一個指針,指向具有特定簽名的函數。 以上例子定義了一個指針 `f`,它可以指向某個接受一個類型為 `const char*` 的參數且返回一個類型為 `int` 的值的函數。 定義完成后,匹配此簽名的函數均可賦值給這個指針。 這個例程就是先將 `std::atoi()` 賦值給 `f`,然后再將它重賦值為 `std::strlen()`。 注意,給定的數據類型并不需要精確匹配:雖然 `std::strlen()` 是以 `std::size_t` 作為返回類型的,但是它也可以被賦值給 `f`。 因為 `f` 是一個函數指針,所以被賦值的函數可以通過重載的 `operator()()` 操作符來調用。 取決于當前被賦值的是哪一個函數,在以上例子中將調用 `std::atoi()` 或 `std::strlen()`。 如果 `f` 未賦予一個函數而被調用,則會拋出一個 `boost::bad_function_call` 異常。 ``` #include <boost/function.hpp> #include <iostream> int main() { try { boost::function<int (const char*)> f; f(""); } catch (boost::bad_function_call &ex) { std::cout << ex.what() << std::endl; } } ``` * [下載源代碼](src/3.4.2/main.cpp) 注意,將值 0 賦給一個 `boost::function` 類型的函數指針,將會釋放當前所賦的函數。 釋放之后再調用它也會導致 `boost::bad_function_call` 異常被拋出。 要檢查一個函數指針是否被賦值某個函數,可以使用 `empty()` 函數或 `operator bool()` 操作符。 通過使用 Boost.Function,類成員函數也可以被賦值給類型為 `boost::function` 的對象。 ``` #include <boost/function.hpp> #include <iostream> struct world { void hello(std::ostream &os) { os << "Hello, world!" << std::endl; } }; int main() { boost::function<void (world*, std::ostream&)> f = &world::hello; world w; f(&w, boost::ref(std::cout)); } ``` * [下載源代碼](src/3.4.3/main.cpp) 在調用這樣的一個函數時,傳入的第一個參數表示了該函數被調用的那個特定對象。 因此,在模板定義中的左括號后的第一個參數必須是該特定類的指針。 接下來的參數才是表示相應的成員函數的簽名。 這個程序還使用了來自 Boost.Ref 庫的 `boost::ref()`,它提供了一個方便的機制向 Boost.Function 傳遞引用。 ## 3.5.?Boost.Lambda 匿名函數 - 又稱為 lambda 函數 - 已經在多種編程語言中存在,但 C++ 除外。 不過在 [Boost.Lambda](http://www.boost.org/libs/lambda/) 庫的幫助下,現在在 C++ 應用中也可以使用它們了。 lambda 函數的目標是令源代碼更為緊湊,從而也更容易理解。 以本章第一節中的代碼例子為例。 ``` #include <iostream> #include <vector> #include <algorithm> void print(int i) { std::cout << i << std::endl; } int main() { std::vector<int> v; v.push_back(1); v.push_back(3); v.push_back(2); std::for_each(v.begin(), v.end(), print); } ``` * [下載源代碼](src/3.2.1/main.cpp) 這段程序接受容器 `v` 中的元素并使用 `print()` 函數將它們寫出到標準輸出流。 由于 `print()` 只是寫出一個簡單的 `int`,所以該函數的實現相當簡單。 嚴格來說,它是如此地簡單,以致于如果可以在 `std::for_each()` 算法里面直接定義它的話,會更為方便; 從而省去增加一個函數的需要。 另外一個好處是代碼更為緊湊,使得算法與負責數據輸出的函數不是局部性分離的。 Boost.Lambda 正好使之成為現實。 ``` #include <boost/lambda/lambda.hpp> #include <iostream> #include <vector> #include <algorithm> int main() { std::vector<int> v; v.push_back(1); v.push_back(3); v.push_back(2); std::for_each(v.begin(), v.end(), std::cout << boost::lambda::_1 << "\n"); } ``` * [下載源代碼](src/3.5.1/main.cpp) Boost.Lambda 提供了幾個結構來定義匿名函數。 代碼就被置于執行的地方,從而省去將它包裝為一個函數再進行相應的函數調用的這些開銷。 與原來的例子一樣,這個程序將容器 `v` 的所有元素寫出至標準輸出流。 與 Boost.Bind 相類似,Boost.Lambda 也定義了三個占位符,名為 `_1`, `_2` 和 `_3`。 但與 Boost.Bind 不同的是,這些占位符是定義在單獨的名字空間的。 因此,該例中的第一個占位符是通過 `boost::lambda::_1` 來引用的。 為了滿足編譯器的要求,必須包含相應的頭文件 `boost/lambda/lambda.hpp`。 雖然代碼的位置位于 `std::for_each()` 的第三個參數處,看起來很怪異,但 Boost.Lambda 可以寫出正常的 C++ 代碼。 通過使用占位符,容器 `v` 的元素可以通過 `&lt;&lt;` 傳給 `std::cout` 以將它們寫出到標準輸出流。 雖然 Boost.Lambda 非常強大,但也有一些缺點。 要在以上例子中插入換行的話,必須用 "\n" 來替代 `std::endl` 才能成功編譯。 因為一元 `std::endl` 模板函數所要求的類型不同于 lambda 函數 `std::cout &lt;&lt; boost::lambda::_1` 的函數,所以在此不能使用它。 下一個版本的 C++ 標準很可能會將 lambda 函數作為 C++ 語言本身的組成部分加入,從而消除對單獨的庫的需要。 但是在下一個版本到來并被不同的編譯器廠商所采用可能還需要好幾年。 在此之前,Boost.Lambda 被證明是一個完美的替代品,從以下例子可以看出,這個例子只將大于1的元素寫出到標準輸出流。 ``` #include <boost/lambda/lambda.hpp> #include <boost/lambda/if.hpp> #include <iostream> #include <vector> #include <algorithm> int main() { std::vector<int> v; v.push_back(1); v.push_back(3); v.push_back(2); std::for_each(v.begin(), v.end(), boost::lambda::if_then(boost::lambda::_1 > 1, std::cout << boost::lambda::_1 << "\n")); } ``` * [下載源代碼](src/3.5.2/main.cpp) 頭文件 `boost/lambda/if.hpp` 定義了幾個結構,允許在 lambda 函數內部使用 `if` 語句。 最基本的結構是 `boost::lambda::if_then()` 模板函數,它要求兩個參數:第一個參數對條件求值 - 如果為真,則執行第二個參數。 如例中所示,每個參數本身都可以是 lambda 函數。 除了 `boost::lambda::if_then()`, Boost.Lambda 還提供了 `boost::lambda::if_then_else()` 和 `boost::lambda::if_then_else_return()` 模板函數 - 它們都要求三個參數。 另外還提供了用于實現循環、轉型操作符,甚至是 `throw` - 允許 lambda 函數拋出異常 - 的模板函數。 雖然可以用這些模板函數在 C++ 中構造出復雜的 lambda 函數,但是你必須要考慮其它方面,如可讀性和可維護性。 因為別人需要學習并理解額外的函數,如用 `boost::lambda::if_then()` 來替代已知的 C++ 關鍵字 `if` 和 `else`,lambda 函數的好處通常隨著它的復雜性而降低。 多數情況下,更為合理的方法是用熟悉的 C++ 結構定義一個單獨的函數。 ## 3.6.?練習 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. 簡化以下程序,將函數對象 `divide_by` 轉換為一個函數,并將 `for` 循環替換為用一個標準的 C++ 算法來輸出數據: ``` #include &lt;algorithm&gt; #include &lt;functional&gt; #include &lt;vector&gt; #include &lt;iostream&gt; class divide_by : public std::binary_function&lt;int, int, int&gt; { public: int operator()(int n, int div) const { return n / div; } }; int main() { std::vector&lt;int&gt; numbers; numbers.push_back(10); numbers.push_back(20); numbers.push_back(30); std::transform(numbers.begin(), numbers.end(), numbers.begin(), std::bind2nd(divide_by(), 2)); for (std::vector&lt;int&gt;::iterator it = numbers.begin(); it != numbers.end(); ++it) std::cout &lt;&lt; *it &lt;&lt; std::endl; } ``` * [下載源代碼](src/3.6.1/main.cpp) 2. 簡化以下程序,將兩個 `for` 循環都替換為標準的 C++ 算法: ``` #include &lt;string&gt; #include &lt;vector&gt; #include &lt;iostream&gt; int main() { std::vector&lt;std::string&gt; strings; strings.push_back("Boost"); strings.push_back("C++"); strings.push_back("Libraries"); std::vector&lt;int&gt; sizes; for (std::vector&lt;std::string&gt;::iterator it = strings.begin(); it != strings.end(); ++it) sizes.push_back(it-&gt;size()); for (std::vector&lt;int&gt;::iterator it = sizes.begin(); it != sizes.end(); ++it) std::cout &lt;&lt; *it &lt;&lt; std::endl; } ``` * [下載源代碼](src/3.6.2/main.cpp) 3. 簡化以下程序,修改變量 `processors` 的類型,并將 `for` 循環替換為標準的 C++ 算法: ``` #include &lt;vector&gt; #include &lt;iostream&gt; #include &lt;cstdlib&gt; #include &lt;cstring&gt; int main() { std::vector&lt;int(*)(const char*)&gt; processors; processors.push_back(std::atoi); processors.push_back(reinterpret_cast&lt;int(*)(const char*)&gt;(std::strlen)); const char data[] = "1.23"; for (std::vector&lt;int(*)(const char*)&gt;::iterator it = processors.begin(); it != processors.end(); ++it) std::cout &lt;&lt; (*it)(data) &lt;&lt; std::endl; } ``` * [下載源代碼](src/3.6.3/main.cpp)
                  <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>

                              哎呀哎呀视频在线观看