<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之旅 廣告
                # 第?14?章?數據結構 ### 目錄 * [14.1 概述](datastructures.html#datastructures_general) * [14.2 元組](datastructures.html#datastructures_tuple) * [14.3 Boost.Any](datastructures.html#datastructures_any) * [14.4 Boost.Variant](datastructures.html#datastructures_variant) * [14.5 練習](datastructures.html#datastructures_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) 授權 ## 14.1.?概述 在 Boost C++ 庫中, 把一些類型定義為container顯得不太合適, 所以就并沒有放在 [第?13?章 _容器_](containers.html "第?13?章?容器") 里。 而把他們放在本章就比較合適了。 舉例來說, `boost::tuple` 就擴展了 C++ 的數據類型 `std::pair` 用以儲存多個而不只是兩個值。 除了 `boost::tuple`, 這一章還涵蓋了類 `boost::any` 和 `boost::variant` 以儲存那些不確定類型的值。 其中 `boost::any` 類型的變量使用起來就像弱類型語言中的變量一樣靈活。 另一方面, `boost::variant` 類型的變量可以儲存一些預定義的數據類型, 就像我們用 `union` 時候一樣。 ## 14.2.?元組 [Boost.Tuple](http://www.boost.org/libs/tuple/) 庫提供了一個更一般的版本的 `std::pair` —— `boost::tuple` 。 不過 `std::pair` 只能儲存兩個值而已, `boost::tuple` 則給了我們更多的選擇。 ``` #include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple_io.hpp> #include <string> #include <iostream> int main() { typedef boost::tuple<std::string, std::string> person; person p("Boris", "Schaeling"); std::cout << p << std::endl; } ``` * [下載源代碼](src/14.2.1/main.cpp) 為了使用 `boost::tuple`, 你必須要包含頭文件: `boost/tuple/tuple.hpp` 。 若想要讓元組和流一起使用, 你還需要包含頭文件: `boost/tuple/tuple_io.hpp` 才行。 其實, `boost::tuple` 的用法基本上和 `std::pair` 一樣。 就像我們在上面的例子里看到的那樣, 兩個值類型的 `std::string` 通過兩個相應的模板參數存儲在了元組里。 當然 `person` 類型也可以用 `std::pair` 來實現。 所有 `boost::tuple` 類型的對象都可以被寫入流里。 再次強調, 為了使用流操作和各種流操作運算符, 你必須要包含頭文件: `boost/tuple/tuple_io.hpp` 。 顯然,我們的例子會輸出: `(Boris Schaeling)` 。 `boost::tuple` 和 `std::pair` 之間最重要的一點不同點: 元組可以存儲無限多個值! ``` #include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple_io.hpp> #include <string> #include <iostream> int main() { typedef boost::tuple<std::string, std::string, int> person; person p("Boris", "Schaeling", 43); std::cout << p << std::endl; } ``` * [下載源代碼](src/14.2.2/main.cpp) 我們修改了實例, 現在的元組里不僅儲存了一個人的firstname和lastname, 還加上了他的鞋子的尺碼。 現在, 我們的例子將會輸出: `(Boris Schaeling 43)` 。 就像 `std::pair` 有輔助函數 `std::make_pair()` 一樣, 一個元組也可以用它的輔助函數 `boost::make_tuple()` 來創建。 ``` #include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple_io.hpp> #include <iostream> int main() { std::cout << boost::make_tuple("Boris", "Schaeling", 43) << std::endl; } ``` * [下載源代碼](src/14.2.3/main.cpp) 就像下面的例子所演示的那樣, 一個元組也可以存儲引用類型的值。 ``` #include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple_io.hpp> #include <string> #include <iostream> int main() { std::string s = "Boris"; std::cout << boost::make_tuple(boost::ref(s), "Schaeling", 43) << std::endl; } ``` * [下載源代碼](src/14.2.4/main.cpp) 因為 "Schaeling" 和 43 是按值傳遞的,所以就直接存儲在了元組中。 與他們不同的是: person 的第一個元素是一個指向 `s` 的引用。 Boost.Ref 中的 `boost::ref()` 就是用來創建這樣的引用的。 相對的, 要創建一個常量的引用的時候, 你需要使用 `boost::cref()` 。 在學習了創建元組的方法之后, 讓我們來了解一下訪問元組中元素的方式。 `std::pair` 只包含兩個元素, 故可以使用屬性 `first` 和 `second` 來訪問其中的元素。 但元組可以包含無限多個元素, 顯然, 我們需要用另一種方式來解決訪問的問題。 ``` #include <boost/tuple/tuple.hpp> #include <string> #include <iostream> int main() { typedef boost::tuple<std::string, std::string, int> person; person p = boost::make_tuple("Boris", "Schaeling", 43); std::cout << p.get<0>() << std::endl; std::cout << boost::get<0>(p) << std::endl; } ``` * [下載源代碼](src/14.2.5/main.cpp) 我們可以用兩種方式來訪問元組中的元素: 使用成員函數 `get()` , 或者將元組傳給一個獨立的函數 `boost::get()` 。 使用這兩種方式時, 元素的索引值都是通過模板參數來指定的。 例子中就分別使用了這兩種方式來訪問 `p` 中的第一個元素。 因此, `Boris` 會被輸出兩次。 另外, 對于索引值合法性的檢查會在編譯期執行, 故訪問非法的索引值會引起編譯期錯誤而不是運行時的錯誤。 對于元組中元素的修改, 你同樣可以使用 `get()` 和 `boost::get()` 函數。 ``` #include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple_io.hpp> #include <string> #include <iostream> int main() { typedef boost::tuple<std::string, std::string, int> person; person p = boost::make_tuple("Boris", "Schaeling", 43); p.get<1>() = "Becker"; std::cout << p << std::endl; } ``` * [下載源代碼](src/14.2.6/main.cpp) `get()` 和 `boost::get()` 都會返回一個引用值。 例子中修改了 lastname 之后將會輸出: `(Boris Becker 43)` 。 Boost.Tuple 除了重載了流操作運算符以外, 還為我們提供了比較運算符。 為了使用它們, 你必須要包含相應的頭文件: `boost/tuple/tuple_comparison.hpp` 。 ``` #include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple_comparison.hpp> #include <string> #include <iostream> int main() { typedef boost::tuple<std::string, std::string, int> person; person p1 = boost::make_tuple("Boris", "Schaeling", 43); person p2 = boost::make_tuple("Boris", "Becker", 43); std::cout << (p1 != p2) << std::endl; } ``` * [下載源代碼](src/14.2.7/main.cpp) 上面的例子將會輸出 `1` 因為兩個元組 `p1` 和 `p2` 是不同的。 同時, 頭文件 `boost/tuple/tuple_comparison.hpp` 還定義了一些其他的比較操作, 比如用來做字典序比較的大于操作等。 Boost.Tuple 還提供了一種叫做 Tier 的特殊元組。 Tier 的特殊之處在于它包含的所有元素都是引用類型的。 它可以通過構造函數 `boost::tie()` 來創建。 ``` #include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple_io.hpp> #include <string> #include <iostream> int main() { typedef boost::tuple<std::string&, std::string&, int&> person; std::string firstname = "Boris"; std::string surname = "Schaeling"; int shoesize = 43; person p = boost::tie(firstname, surname, shoesize); surname = "Becker"; std::cout << p << std::endl; } ``` * [下載源代碼](src/14.2.8/main.cpp) 上面的例子創建了一個 tier `p`, 他包含了三個分別指向 `firstname`, `surname` 和 `shoesize` 的引用值。 在修改變量 `surname` 的同時, tier 也會跟著改變。 就像下面的例子展示的那樣,你當然可以用 `boost::make_tuple()` 和 `boost::ref()` 來代替構造函數 `boost::tie()` 。 ``` #include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple_io.hpp> #include <string> #include <iostream> int main() { typedef boost::tuple<std::string&, std::string&, int&> person; std::string firstname = "Boris"; std::string surname = "Schaeling"; int shoesize = 43; person p = boost::make_tuple(boost::ref(firstname), boost::ref(surname), boost::ref(shoesize)); surname = "Becker"; std::cout << p << std::endl; } ``` * [下載源代碼](src/14.2.9/main.cpp) `boost::tie()` 在一定程度上簡化了語法, 同時, 也可以用作“拆箱”元組。 在接下來的這個例子里, 元組中的各個元素就被很方便的“拆箱”并直接賦給了其他變量。 ``` #include <boost/tuple/tuple.hpp> #include <string> #include <iostream> boost::tuple<std::string, int> func() { return boost::make_tuple("Error message", 2009); } int main() { std::string errmsg; int errcode; boost::tie(errmsg, errcode) = func(); std::cout << errmsg << ": " << errcode << std::endl; } ``` * [下載源代碼](src/14.2.10/main.cpp) 通過使用 `boost::tie()` , 元組中的元素:字符串“Error massage”和錯誤代碼“2009”就很方便地經 `func()` 的返回值直接賦給了 `errmsg` 和 `errcode` 。 ## 14.3.?Boost.Any 像 C++ 這樣的強類型語言要求給每個變量一個確定的類型。 而以 JavaScript 為代表的弱類型語言卻不這樣做, 弱類型的每個變量都可以存儲數組、 布爾值、 或者是字符串。 庫 [Boost.Any](http://www.boost.org/libs/any/) 給我們提供了 `boost::any` 類, 讓我們可以在 C++ 中像 JavaScript 一樣的使用弱類型的變量。 ``` #include <boost/any.hpp> int main() { boost::any a = 1; a = 3.14; a = true; } ``` * [下載源代碼](src/14.3.1/main.cpp) 為了使用 `boost::any`, 你必須要包含頭文件: `boost/any.hpp`。 接下來, 你就可以定義和使用 `boost::any` 的對象了。 需要注明的是: `boost::any` 并不能真的存儲任意類型的值; Boost.Any 需要一些特定的前提條件才能工作。 任何想要存儲在 `boost::any` 中的值,都必須是可拷貝構造的。 因此,想要在 `boost::any` 存儲一個字符串類型的值, 就必須要用到 `std::string` , 就像在下面那個例子中做的一樣。 ``` #include <boost/any.hpp> #include <string> int main() { boost::any a = 1; a = 3.14; a = true; a = std::string("Hello, world!"); } ``` * [下載源代碼](src/14.3.2/main.cpp) 如果你企圖把字符串 "Hello, world!" 直接賦給 `a` , 你的編譯器就會報錯, 因為由基類型 `char` 構成的字符串在 C++ 中并不是可拷貝構造的。 想要訪問 `boost::any` 中具體的內容, 你必須要使用轉型操作: `boost::any_cast` 。 ``` #include <boost/any.hpp> #include <iostream> int main() { boost::any a = 1; std::cout << boost::any_cast<int>(a) << std::endl; a = 3.14; std::cout << boost::any_cast<double>(a) << std::endl; a = true; std::cout << boost::any_cast<bool>(a) << std::endl; } ``` * [下載源代碼](src/14.3.3/main.cpp) 通過由模板參數傳入 `boost::any_cast` 的值, 變量會被轉化成相應的類型。 一旦你指定了一種非法的類型, 該操作會拋出 `boost::bad_any_cast` 類型的異常。 ``` #include <boost/any.hpp> #include <iostream> int main() { try { boost::any a = 1; std::cout << boost::any_cast<float>(a) << std::endl; } catch (boost::bad_any_cast &e) { std::cerr << e.what() << std::endl; } } ``` * [下載源代碼](src/14.3.4/main.cpp) 上面的例子就拋出了一個異常, 因為 `float` 并不能匹配原本存儲在 `a` 中的 `int` 類型。 記住, 在任何情況下都保證 `boost::any` 中的類型匹配是很重要的。 在沒有通過模板參數指定 `short` 或 `long` 類型時, 同樣會有異常拋出。 既然 `boost::bad_any_cast` 繼承自 `std::bad_cast`, `catch` 當然也可以捕獲相應類型的異常。 想要檢查 `boost::any` 是否為空, 你可以使用 `empty()` 函數。 想要確定其中具體的類型信息, 你可以使用 `type()` 函數。 ``` #include <boost/any.hpp> #include <typeinfo> #include <iostream> int main() { boost::any a = 1; if (!a.empty()) { const std::type_info &ti = a.type(); std::cout << ti.name() << std::endl; } } ``` * [下載源代碼](src/14.3.5/main.cpp) 上面的例子同時用到了 `empty()` 和 `type()` 函數。 `empty()` 將會返回一個布爾值, 而 `type()` 則會返回一個在 `typeinfo` 中定義的 `std::type_info` 值。 作為對這一節的總結, 最后一個例子會向你展示怎樣用 `boost::any_cast` 來定義一個指向 `boost::any` 中內容的指針。 ``` #include <boost/any.hpp> #include <iostream> int main() { boost::any a = 1; int *i = boost::any_cast<int>(&a); std::cout << *i << std::endl; } ``` * [下載源代碼](src/14.3.6/main.cpp) 你需要做的就是傳遞一個 `boost::any` 類型的指針, 作為 `boost::any_cast` 的參數; 模板參數卻沒有任何改動。 ## 14.4.?Boost.Variant [Boost.Variant](http://www.boost.org/libs/variant/) 和 Boost.Any 之間的不同點在于 Boost.Any 可以被視為任意的類型, 而 Boost.Variant 只能被視為固定數量的類型。 讓我們來看下面這個例子。 ``` #include <boost/variant.hpp> int main() { boost::variant<double, char> v; v = 3.14; v = 'A'; } ``` * [下載源代碼](src/14.4.1/main.cpp) Boost.Variant 為我們提供了一個定義在 `boost/variant.hpp` 中的類: `boost::variant` 。 既然 `boost::variant` 是一個模板, 你必須要指定至少一個參數。 Variant 所存儲的數據類型就由這些參數來指定。 上面的例子就給 `v` 指定了 `double` 類型和 `char` 類型。 注意, 一旦你將一個 `int` 值賦給了 `v`, 你的代碼將不會編譯通過。 當然, 上面的例子也可以用一個 `union` 類型來實現, 但是與 union 不同的是: `boost::variant` 可以儲存像 `std::string` 這樣的 class 類型的數據。 ``` #include <boost/variant.hpp> #include <string> int main() { boost::variant<double, char, std::string> v; v = 3.14; v = 'A'; v = "Hello, world!"; } ``` * [下載源代碼](src/14.4.2/main.cpp) 要訪問 `v` 中的數據, 你可以使用獨立的 `boost::get()` 函數。 ``` #include <boost/variant.hpp> #include <string> #include <iostream> int main() { boost::variant<double, char, std::string> v; v = 3.14; std::cout << boost::get<double>(v) << std::endl; v = 'A'; std::cout << boost::get<char>(v) << std::endl; v = "Hello, world!"; std::cout << boost::get<std::string>(v) << std::endl; } ``` * [下載源代碼](src/14.4.3/main.cpp) `boost::get()` 需要傳入一個模板參數來指明你需要返回的數據類型。 若是指定了一個非法的類型, 你會遇到一個運行時而不是編譯期的錯誤。 所有 `boost::variant` 類型的值都可以被直接寫入標準輸入流這樣的流中, 這可以在一定程度上讓你避開運行時錯誤的風險。 ``` #include <boost/variant.hpp> #include <string> #include <iostream> int main() { boost::variant<double, char, std::string> v; v = 3.14; std::cout << v << std::endl; v = 'A'; std::cout << v << std::endl; v = "Hello, world!"; std::cout << v << std::endl; } ``` * [下載源代碼](src/14.4.4/main.cpp) 想要分別處理各種不同類型的數據, Boost.Variant 為我們提供了一個名為 `boost::apply_visitor()` 的函數。 ``` #include <boost/variant.hpp> #include <boost/any.hpp> #include <vector> #include <string> #include <iostream> std::vector<boost::any> vector; struct output : public boost::static_visitor<> { void operator()(double &d) const { vector.push_back(d); } void operator()(char &c) const { vector.push_back(c); } void operator()(std::string &s) const { vector.push_back(s); } }; int main() { boost::variant<double, char, std::string> v; v = 3.14; boost::apply_visitor(output(), v); v = 'A'; boost::apply_visitor(output(), v); v = "Hello, world!"; boost::apply_visitor(output(), v); } ``` * [下載源代碼](src/14.4.5/main.cpp) `boost::apply_visitor()` 第一個參數需要傳入一個繼承自 `boost::static_visitor` 類型的對象。 這個類必須要重載 `operator()()` 運算符來處理 `boost::variant` 每個可能的類型。 相應的, 例子中的 `v` 就重載了三次 operator() 來處理三種可能的類型: `double`, `char` 和 `std::string`。 再仔細看代碼, 不難發現 `boost::static_visitor` 是一個模板。 那么,當 `operator()()` 有返回值的時候, 就必須返回一個模板才行。 如果 operator() 像例子那樣沒有返回值時, 你就不需要模板了。 `boost::apply_visitor()` 的第二個參數是一個 `boost::variant` 類型的值。 在使用時, `boost::apply_visitor()` 會自動調用跟第二個參數匹配的 `operator()()` 。 示例程序中的 `boost::apply_visitor()` 就自動調用了三個不同的 operator 第一個是 `double` 類型的, 第二個是 `char` 最后一個是 `std::string`。 `boost::apply_visitor()` 的優點不只是“自動調用匹配的函數”這一點。 更有用的是, `boost::apply_visitor()` 會確認是否 `boost::variant` 中的每個可能值都定義了相應的函數。 如果你忘記重載了任何一個函數, 代碼都不會編譯通過。 當然, 如果對每種類型的操作都是一樣的, 你也可以像下面的示例一樣使用一個模板來簡化你的代碼。 ``` #include <boost/variant.hpp> #include <boost/any.hpp> #include <vector> #include <string> #include <iostream> std::vector<boost::any> vector; struct output : public boost::static_visitor<> { template <typename T> void operator()(T &t) const { vector.push_back(t); } }; int main() { boost::variant<double, char, std::string> v; v = 3.14; boost::apply_visitor(output(), v); v = 'A'; boost::apply_visitor(output(), v); v = "Hello, world!"; boost::apply_visitor(output(), v); } ``` * [下載源代碼](src/14.4.6/main.cpp) 既然 `boost::apply_visitor()` 可以在編譯期確定代碼的正確性, 你就該更多的使用它而不是 `boost::get()`。 ## 14.5.?練習 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. 自定義一種數據類型: `configuration` 它可以存儲一個 name-value 對。 Name 為 `std::string` 類型, 而 value 可為 `std::string` 或者 `int` 或者 `float` 類型。 在 `main()` 函數里, 用 `configuration` 存儲下列 name-value 對: path=C:\Windows, version=3, pi=3.1415。 通過向便準輸出流輸出來驗證你對數據類型的設計。 2. 在輸出后, 將對象中的 path 修改為 C:\Windows\System。 再次向標準輸出流輸出以驗證你的設計。
                  <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>

                              哎呀哎呀视频在线观看