1. 程式人生 > >現代C++教程:高速上手(四)-容器

現代C++教程:高速上手(四)-容器

## 1、線性容器 std::array與std::vector不同的是,array物件的大小是固定的,如果容器大小是固定的,那麼可以優先考慮使用std::array容器。 由於std::vector是自動擴容的,當存入大量的資料後,並且對容器進行了刪除操作,容器並不會自動歸還被刪除元素相應的記憶體,這時候需要手動執行shrink_to_fit()釋放這部分記憶體。 std::array C風格介面傳參: ```cpp void foo(int *p, int len){ return; } std::array arr = {1,2,3,4}; //foo(arr,arr.size()); //非法,無法隱式轉換 foo(&arr[0], arr.size()); foo(arr.data(), arr.size()); //使用std::sort std::sort(arr.begin(), arr.end()); //升序 std::sort(arr.begin(), arr.end(), [](int a, int b){ return b > a; }) ``` std::forward_list是一個列表容器,使用方法和std::list基本類似。和list的雙向連結串列的實現不同,forward_list使用單向連結串列進行實現,提供了O(1)複雜度的元素插入,不支援快速隨機訪問,也是標準庫容器中唯一一個不提供size()方法的容器。當不需要雙向迭代時,具有比list更高的空間利用率。 ## 2、無序容器 傳統c++中的有序容器 std::map / std::set,這些元素內部通過紅黑樹進行實現,插入和搜尋的平均複雜度均為O(log(size))。在插入元素時,會根據<操作符比較元素大小並判斷元素是否相同,並選擇合適的位置插入到容器中。當對這個容器中的元素進行遍歷時,輸出結果會按照<操作符的順序來逐個遍歷。 而無序容器中的元素是不進行排序的,內部通過Hash表實現,插入和搜尋元素的平均複雜度為O(constant),在不關心容器內部元素順序時,能夠獲得顯著的效能提升。 c++11引入了兩組無序容器:std::unordered_map / std::unordered_multimap和std::unordered_set / std::unordered_multiset。 它們的用法和原有的std::map / std::multimap / std::set / std::multiset基本類似。 ```cpp #include #include #include #include using namespace std; int main(){ unordered_map u = { {1, "1"}, {3, "3"}, {2, "2"} }; map v = { {1, "1"}, {3, "3"}, {2, "2"} }; cout << "std::unordered_map" << endl; for(const auto &n : u){ cout << "Key:[" << n.first << "] Value:[" << n.second << "]\n"; } cout < #include using namespace std; auto get_student(int id){ switch (id) { case 0: return make_tuple(3.8, 'A', "張三"); break; case 1: return make_tuple(2.9, 'C', "李四"); break; case 2: return make_tuple(1.7, 'D', "王五"); break; default: return make_tuple(0.0, 'D', "null"); break; } } int main(){ auto student = get_student(0); std::cout << "ID: 0, " << "GPA: " << get<0>(student) << ", " << "成績:" << get<1>(student) << ", " << "姓名:" << get<2>(student) << "\n"; double gpa; char grade; string name; //元祖進行拆包 tie(gpa, grade, name) = get_student(1); std::cout << "ID: 1, " << "GPA: " << gpa << ", " << "成績:" << grade << ", " << "姓名:" << name << "\n"; return 0; } ``` std::get除了使用常量獲取元組物件外,c++14增加了使用型別來獲取元組中的物件: ```cpp std::tuple t("123", 4.5, 6.7, 8); std::cout << std::get(t) << std::endl; std::cout << std::get(t) << std::endl; //非法,引發編譯期錯誤 std::cout << std::get(t) << std::endl; ``` ### 執行期索引 std::get<>依賴一個編譯期的常量,所以下面的方式是不合法的: ```cpp int index = 1; std::get(t); //非法 ``` c++17引入了std::variant<>,提供給variant<>的型別模版引數 可以讓一個variant<>從而容納提供的幾種型別的變數(在其他語言,例如Python/JavaScrpit等,表現為動態型別): ```cpp #include template constexpr std::variant _tuple_index(const std::tuple& tpl, size_t i){ if constexpr(n >= sizeof...(T)) throw std::out_of_range("越界."); if(i == n) return std::variant{ std::in_place_index, std::get(tpl) }; return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(tpl, i); } template constexpr std::variant tuple_index(const std::tuple& tpl, size_t i){ return _tuple_index<0>(tpl, i); } template std::ostream & operator<< (std::ostream & s, std::variant const & v){ std::visit([&](auto && x){s< auto tuple_len(T &tpl){ return std::tuple_size::value; } ``` 這樣就能夠對元組進行迭代了: ```cpp //迭代 for(int i = 0; i != tuple_len(new_tuple); ++i){ //執行期索引 std::cout << tuple_index