C++ 11 新特性之容器相關特性
這是C++11新特性介紹的第四部分,涉及到C++11這次更新中與容器有關的新特性。
不想看toy code的讀者可以直接拉到文章最後看這部分的總結。
cbegin和cend
原來的begin和end返回的iterator是否是常量取決於對應的容器型別,但是有時,即使容器不是常量型別,我們也希望獲得一個const_iterator,以避免不必要的修改行為。C++11新標準中提供了cbegin和cend函式,無論容器型別,都固定返回const_iterator。
vector<int> c1 = {0, 1, 2, 3, 4}; auto it1_1 = c1.begin(); auto it1_2 = c1.cbegin(); *it1_1 = 4; //*it1_2 = 5; // wrong, const iterator's value can't be changed via this iterator. cout<<*it1_1<<'\t'<<*it1_2<<endl;
標準庫的begin和end
C++11新標準提供了begin和end函式,可以對普通陣列使用,獲得頭指標和尾指標。
int c2[] = {0, 1, 2, 3, 4}; auto it2_1 = begin(c2); auto it2_2 = end(c2); while(it2_1 != it2_2) { cout<<*it2_1<<'\t'; it2_1++; } cout<<endl;
新的賦值方式
C++11允許使用一個{}包圍的初始值列表來進行復制。如果等號左側是個容器,那麼怎麼賦值由容器決定。
vector<int> c3; c3 = {0, 1, 2, 3, 4}; for(auto it3_1 = c3.begin(); it3_1 != c3.end(); it3_1++) cout<<*it3_1<<'\t'; cout<<endl;
initializer_list
C++11新標準中新增了initializer_list型別,其實在之前介紹初始化的那篇文章中,使用vector v = {0, 1, 2, 3, 4}這種初始化形式時,就隱式的使用了initializer_list:每當在程式中出現一段以{}包圍的字面量時,就會自動構造一個initializer_list物件。
另外,initializer_list的另一個作用就在於作為函式的形參,這樣的函式可以方便的傳入以{}包圍的不定長列表:
void print_list(initializer_list<int> il) { for(auto it = il.begin(); it != il.end(); it++) { cout<<*it<<'\t'; //*it = 100; // wrong. initializer_list element is read-only. } cout<<endl; } print_list({0, 1, 2, 3, 4}); print_list({0, 1, 2, 3, 4, 5});
但是,需要注意的是,initializer_list中的元素是隻讀的。
array
C++11標準中提供了定長陣列容器array,相比於普通陣列更安全、更易使用。array是定長陣列,所以不支援諸如插入、刪除等改變容器大小的操作,但是可以對元素進行賦值改變其值。
array<int, 5> c4 = {0, 1, 2, 3, 4}; c4[3] = 100; // can't insert since the array size is fixed. for(auto it4_1 = c4.begin(); it4_1 != c4.end(); it4_1++) { cout<<*it4_1<<'\t'; } cout<<endl;
forward_list
C++11標準中增加了新的容器forward_list,提供了一個快速的、安全的單向連結串列實現。因為是單向連結串列,所以也就沒有rbegin、rend一類的函式支援了。
同樣是因為單向連結串列的緣故,無法訪問到給定元素的前驅,所以沒有提供insert函式,而對應提供了一個insert_after函式,用於在給定元素之後插入節點。erase_after、emplace_after同理。
forward_list<int> c5 = {3, 4}; c5.push_front(2); c5.push_front(1); auto it5_1 = c5.before_begin(); c5.insert_after(it5_1, 0); for(auto it5_2 = c5.begin(); it5_2 != c5.end(); it5_2++) { cout<<*it5_2<<'\t'; } cout<<endl;
swap
新標準中提供了非成員版本的swap操作,此操作對array容器,會交換元素的值;對其他容器,則只交換容器的內部結構,並不進行元素值的拷貝操作,所以在這種情況下是非常迅速的。
正因如此,當swap array後,原來array上的迭代器還依然指向原有元素,只是元素的值變了;
而swap非array容器之後,原來容器上的迭代器將指向對方容器上的元素,而指向的元素的值卻保持不變。
vector<int> c6 = {0, 1, 2, 3, 4}; vector<int> c7 = {5, 6, 7, 8, 9}; auto it6_1 = c6.begin(); auto it7_1 = c7.begin(); swap(c6, c7); for(auto it6_2 = c6.begin(); it6_2 != c6.end(); it6_2++) cout<<*it6_2<<'\t'; cout<<endl; for(auto it7_2 = c7.begin(); it7_2 != c7.end(); it7_2++) cout<<*it7_2<<'\t'; cout<<endl; cout<<(it6_1 == c7.begin())<<'\t'<<(it7_1 == c6.begin())<<endl; array<int, 5> c8 = {0, 1, 2, 3, 4}; array<int, 5> c9 = {5, 6, 7, 8, 9}; auto it8_1 = c8.begin(); auto it9_1 = c9.begin(); swap(c8, c9); cout<<(it8_1 == c8.begin())<<'\t'<<(it9_1 == c9.begin())<<endl;
emplace
emplace操作將使用接受的引數構造一個對應容器中的元素,並插入容器中。這一點,使用普通的insert、push操作是做不到的。
class TestData { public: TestData(string name, int age, double salary): name(name), age(age), salary(salary) {} private: string name; int age; double salary; }; vector<TestData> c10; c10.emplace_back("yubo", 26, 100000000000.0); //c10.push_back("laowang", 56, 10.5); // wrong. no 3 params push_back c10.push_back(TestData("laowang", 56, 10.5)); cout<<c10.size()<<endl;
shrink_to_fit
一般可變長容器會預先多分配一部分記憶體出來,以備在後續增加元素時,不用每次都申請記憶體。所以有size和capacity之分。size是當前容器中存有元素的個數,而capacity則是在不重新申請記憶體的情況下,當前可存放元素的最大數目。而shrink_to_fit就表示將capacity中的多餘部分退回,使其回到size大小。但是,這個函式的具體效果要依賴於編譯器的實現……
vector<int> c11; for(int i = 0; i < 24; i++) c11.push_back(i); cout<<c11.size()<<'\t'<<c11.capacity()<<endl; c11.shrink_to_fit(); cout<<c11.size()<<'\t'<<c11.capacity()<<endl;
無序關聯容器
C++11新標準中引入了對map、set等關聯容器的無序版本,叫做unorderer_map\/unordered_set。
無序關聯容器不使用鍵值的比較操作來組織元素順序,而是使用雜湊。這樣在某些元素順序不重要的情況下,效率更高。
unordered_map<string, int> c12; map<string, int> c13; string string_keys[5] = {"aaa", "bbb", "ccc", "ddd", "eee"}; for(int i = 0; i < 5; i++) { c12[string_keys[i]] = i; c13[string_keys[i]] = i; } cout<<"normal map:\n"; for(auto it13 = c13.begin(); it13 != c13.end(); it13++) cout<<it13->first<<':'<<it13->second<<'\t'; cout<<endl; cout<<"unordered map:\n"; for(auto it12 = c12.begin(); it12 != c12.end(); it12++) cout<<it12->first<<':'<<it12->second<<'\t'; cout<<endl;
tuple
熟悉python的程式設計師應該對tuple都不陌生,C++11中也引入了這一資料結構,用於方便的將不同型別的值組合起來。
可以通過如下方式,獲取tuple中的元素、tuple的長度等:
//tuple<int, string, vector<int>> c14 = {1, "tuple", {0, 1, 2, 3, 4}}; // wrong. must explicit initialize tuple<int, string, vector<int>> c14{1, "tuple", {0, 1, 2, 3, 4}}; get<0>(c14) = 2; typedef decltype(c14) ctype; size_t sz = tuple_size<ctype>::value; cout<<get<0>(c14)<<'\t'<<get<1>(c14)<<'\t'<<get<2>(c14)[0]<<'\t'<<sz<<endl;
總結
- cbegin和cend提供了固定獲取const_iterator的方式。
- begin和end用於普通陣列獲得首尾指標。
- 可以使用{}包圍的初始值列表進行賦值。
- 增加initializer_list型別用於方便的使用{}包圍的不定長列表。
- 增加新的定長陣列容器array 單向連結串列容器forward_list。
- 增加非成員函式版本的swap操作。對array swap只交換元素值,而容器的結構不變;對其他容器則只改變容器資料結構,而元素值不變。
- 增加emplace操作用於將引數傳遞給建構函式構造元素並插入容器。
- 增加shrink_to_fit函式用於退回多餘的空間。
- 增加無序關聯容器。
- 增加tuple容器。
完整程式碼詳見container.cpp
相關文章
- C++ 11 新特性之ClassC++
- C++ 11 新特性之隨機數庫C++隨機
- C++ 11 新特性之正規表示式C++
- C++ 11 新特性 nullptr 學習C++Null
- C++新特性C++
- C++整理20_C++11新特性C++
- C++11 新特性之 lambdaC++
- IOS11新特性之maskedCornersiOS
- 11g新特性 密碼錯誤驗證延遲特性的相關驗證密碼
- Oracle11新特性——PLSQL新特性(七)OracleSQL
- Oracle11新特性——PLSQL新特性(六)OracleSQL
- Oracle11新特性——PLSQL新特性(五)OracleSQL
- Oracle11新特性——PLSQL新特性(四)OracleSQL
- Oracle11新特性——PLSQL新特性(三)OracleSQL
- Oracle11新特性——PLSQL新特性(二)OracleSQL
- Oracle11新特性——PLSQL新特性(一)OracleSQL
- c++ 11 執行緒池---完全使用c++ 11新特性C++執行緒
- C++ 2.0新特性C++
- C++的新特性C++
- Oracle 11g 新特性之DRCPOracle
- Oracle11g新特性之editionOracle
- Oracle 11g新特性之SecureFilesOracle
- Java 11新特性Java
- 【ORACLE新特性】11G 分割槽新特性Oracle
- MySQL 8.0 18個管理相關的新特性MySql
- Oracle 18c新特性詳解 - 表和表空間相關的新特性Oracle
- Oracle 21c新特性預覽與日常管理相關的幾個新特性Oracle
- innodbbufferpool相關特性
- C++ 11 新特性之型別推斷與型別獲取C++型別
- C++11新特性(二):語言特性C++
- C++11新特性(一):語言特性C++
- C++11新特性(三):語言特性C++
- C++11 新特性之智慧指標C++指標
- C++11新特性之Lambda表示式C++
- C++11新特性之智慧指標C++指標
- Oracle11.2新特性之儲存Oracle
- C++11新特性C++
- MySQL 5.7 學習心得之安全相關特性MySql