C++中STL介紹
轉載自:https://blog.csdn.net/hhu1506010220/article/details/51971642
介紹
這篇文章的目的是為了介紹std::vector,如何恰當地使用它們的成員函數等操作。本文中還討論了條件函數和函數指針在叠代算法中使用,如在remove_if()和for_each()中的使用。通過閱讀這篇文章讀者應該能夠有效地使用vector容器,而且應該不會再去使用C類型的動態數組了。
Vector總覽
vector是C++標準模板庫中的部分內容,它是一個多功能的,能夠操作多種數據結構和算法的模板類和函數庫。vector之所以被認為是一個容器,是因為它能夠像容器一樣存放各種類型的對象,簡單地說,vector是一個能夠存放任意類型的動態數組,能夠增加和壓縮數據。
Vector成員函數:
函數 | 表述 |
c.assign(begin,end) c.assign(n,element) |
截取c中[begin,end)區間的元素賦值給c 截取c中n個element的拷貝賦值給c |
c.at(idx) | 傳回索idx所指的數據,如果idx越界,拋出out_of_range |
c.back() | 傳回最後一個數據,不檢查這個數據是否存在 |
c.begin() | 傳回叠代器中的第一個數據 |
c.capacity() | 返回容器中的數據個數 |
c.clear() | 移除容器中的所有數據 |
c.empty() | 判斷容器是否為空 |
c.end() | 傳回叠代器中的最後一個數據地址 |
c.earse(pos) c.earse(begin,end) |
刪除pos位置的數據,傳回下一個數據的位置 刪除[begin,end)區間的數據,傳回下一個數據的位置 |
c.front() | 傳回第一個數據 |
get_allocator | 使用構造函數返回一個拷貝 |
c.insert(pos,element) c.insert(pos,n,element) c.insert(pos,begin,end) |
在pos位置插入一個element拷貝,傳回新數據位置 在pos位置插入n個element數據,無返回值 在pos位置插入【begin,end)區間的數據,無返回值 |
c.max_size() | 返回容器中最大數據的數量 |
c.pop_back() | 刪除最後一個數據 |
c.push_back(element) | 在尾部加入一個數據 |
c.rbegin() | 傳回一個逆向隊列的第一個數據 |
c.rend() | 傳回一個逆向隊列的最後一個數據的下一個位置 |
c.resize(num) | 重新制定隊列的長度 |
c.reserve() c.size() c1.swap(c2) swap(c1,c2) vector<element> c vector<element>c1(c2) vector<element>c(n) vector<element>c(n,element) vector<element>c(begin,end) c.~vector<element>() |
保留適當的容量 返回容器中實際數據的個數 將c1和c2元素互換 同上 創建一個空的vector 復制一個vector 創建一個vector,含有n個元素,數據均已缺省構造產生 創建一個含有n個element拷貝的vector 創建一個以【begin,end)區間的vector 銷毀所有數據,釋放內存 |
1 vector<string> strv(500);//創建包含500個string類型數據的vector 2 vector<string> strv1(500,"0");//創建包含500個string類型數據的vector,並初始化為"0" 3 vector<string> strv2(strv);
向vector添加一個數據
vector添加數據的缺省方法是push_back()。push_back()函數表示將數據添加到vector的尾部,並按需要來分配內存。
1 vector<int> veri; 2 for(int i=0;i<10;i++){ 3 veri.push_back(i); 4 }
獲取vector中指定位置的數據
很多時候我們不必要知道vector裏面有多少數據,vector裏面的數據是動態分配的,使用push_back()的一系列分配空間常常決定於文件或一些數據源。如果你想知道vector存放了多少數據,你可以使用empty()。獲取vector的大小,可以使用size()。例如,如果你想獲取一個vector v的大小,但不知道它是否為空,或者已經包含了數據,如果為空想設置為-1,你可以使用下面的代碼實現:
1 int nSize = v.empty() ? -1 : static_cast<int>(v.size());
訪問vector中的數據
使用兩種方法來訪問vector。
1、 vector::at()
2、 vector::operator[]
operator[]主要是為了與C語言進行兼容。它可以像C語言數組一樣操作。但at()是我們的首選,因為at()進行了邊界檢查,如果訪問超過了vector的範圍,將拋出一個例外。由於operator[]容易造成一些錯誤,所有我們很少用它,下面進行驗證一下:
1 vector<int> v; 2 v.reserve(10); 3 for(int i=0; i<7; i++) 4 v.push_back(i); 5 try 6 { 7 int iVal1 = v[7]; // not bounds checked - will not throw 8 int iVal2 = v.at(7); // bounds checked - will throw if out of range 9 } 10 catch(const exception& e) 11 { 12 cout << e.what(); 13 }
我們使用reserve()分配了10個int型的空間,但並不沒有初始化。
你可以在這個代碼中嘗試不同條件,觀察它的結果,但是無論何時使用at(),都是正確的。
刪除vector中的數據 vector能夠非常容易地添加數據,也能很方便地取出數據,同樣vector提供了erase(),pop_back(),clear()來刪除數據,當你刪除數據的時候,你應該知道要刪除尾部的數據,或者是刪除所有數據,還是個別的數據。在考慮刪除等操作之前讓我們靜下來考慮一下在STL中的一些應用。 壓縮一個臃腫的vcetor很多時候大量的刪除數據,或者通過使用reserve(),結果vector的空間遠遠大於實際需要的。所有需要壓縮vector到它實際的大小。resize()能夠增加vector的大小。Clear()僅僅能夠改變緩存的大小,所有的這些對於vector釋放內存等九非常重要了。如何來解決這些問題呢,讓我們來操作一下。
我們可以通過一個vector創建另一個vector。讓我們看看這將發生什麽。假定我們已經有一個vector veri,它的內存大小為1000,當我們調用size()的時候,它的大小僅為7。我們浪費了大量的內存。讓我們在它的基礎上創建一個vector。
1 vector<int> veri; 2 veri.reserve(1000); 3 for(int i=0;i<10;i++){ 4 veri.push_back(i); 5 } 6 7 cout << "veri capacity="<<veri.capacity()<<endl; 8 cout << "veri size="<<veri.size()<<endl;
創建新的vector來與veri交換
1 vector<int> veri; 2 veri.reserve(1000); 3 for(int i=0;i<10;i++){ 4 veri.push_back(i); 5 } 6 7 cout << "veri capacity="<<veri.capacity()<<endl; 8 cout << "veri size="<<veri.size()<<endl; 9 10 vector<int> veri1; 11 veri1.swap(veri); 12 13 cout << "veri capacity="<<veri.capacity()<<endl; 14 cout << "veri size="<<veri.size()<<endl; 15 cout << "veri1 capacity="<<veri1.capacity()<<endl; 16 cout << "veri1 size="<<veri1.size()<<endl;
新的vector的capacity變為1000,size為7。這樣讓沒有達到目的
1 vector<int> veri; 2 veri.reserve(1000); 3 for(int i=0;i<10;i++){ 4 veri.push_back(i); 5 } 6 7 cout << "veri capacity="<<veri.capacity()<<endl; 8 cout << "veri size="<<veri.size()<<endl; 9 10 // vector<int> veri1; 11 // veri1.swap(veri); 12 vector<int>(veri1).swap(veri1); 13 cout << "veri capacity="<<veri.capacity()<<endl; 14 cout << "veri size="<<veri.size()<<endl; 15 // cout << "veri1 capacity="<<veri1.capacity()<<endl; 16 // cout << "veri1 size="<<veri1.size()<<endl;
達到目的!!!!
同時,測試一下clear,可以發現clear只是把內容清楚了,但vector的內存並沒有釋放掉,可以使用swap來徹底釋放內存。
1 vector<int> veri; 2 veri.reserve(1000); 3 for(int i=0;i<10;i++){ 4 veri.push_back(i); 5 } 6 cout << "veri capacity="<<veri.capacity()<<endl; 7 cout << "veri size="<<veri.size()<<endl; 8 9 veri.clear(); 10 cout << "veri capacity="<<veri.capacity()<<endl; 11 cout << "veri size="<<veri.size()<<endl; 12 vector<int>(veri).swap(veri); 13 cout << "veri capacity="<<veri.capacity()<<endl; 14 cout << "veri size="<<veri.size()<<endl;
叠代器
1 // vector.cpp : 定義控制臺應用程序的入口點。 2 // 3 4 #include "stdafx.h" 5 #include <vector> 6 #include <iostream> 7 #include <numeric> 8 #include <algorithm> 9 using namespace std; 10 11 bool Comp(const int &a,const int &b){ 12 13 return a > b; 14 } 15 int _tmain(int argc, _TCHAR* argv[]) 16 { 17 vector<int> vec; 18 //添加元素 19 vec.push_back(10); 20 vec.push_back(20); 21 vec.push_back(30); 22 23 vector<int>::iterator vecit ; 24 cout << "初始:"; 25 for(vecit=vec.begin();vecit!=vec.end();vecit++){ 26 cout << *vecit << " "; 27 } 28 cout <<endl; 29 30 cout << "求和="<<accumulate(vec.begin(),vec.end(),0)<<endl; 31 32 //插入 33 vec.insert(vec.begin(),1); 34 vec.insert(vec.end(),40); 35 cout << "插入:"; 36 for(vecit=vec.begin();vecit!=vec.end();vecit++){ 37 cout << *vecit << " "; 38 } 39 cout <<endl; 40 //刪除 41 vec.erase(vec.begin()); 42 cout << "刪除:"; 43 vec.erase(vec.begin(),vec.begin()+2); 44 for(vecit=vec.begin();vecit!=vec.end();vecit++){ 45 cout << *vecit << " "; 46 } 47 cout <<endl; 48 //轉置 49 reverse(vec.begin(),vec.end()); 50 cout << "轉置:"; 51 for(vecit=vec.begin();vecit!=vec.end();vecit++){ 52 cout << *vecit << " "; 53 } 54 cout <<endl; 55 56 vec.push_back(1); 57 vec.push_back(3); 58 vec.push_back(2); 59 vec.push_back(55); 60 vec.push_back(-1); 61 vec.push_back(0); 62 vec.push_back(2); 63 vec.push_back(3); 64 vec.push_back(4); 65 cout << "初始:"; 66 for(vecit=vec.begin();vecit!=vec.end();vecit++){ 67 cout << *vecit << " "; 68 } 69 cout <<endl; 70 sort(vec.begin(),vec.end()); 71 cout << "升序:"; 72 for(vecit=vec.begin();vecit!=vec.end();vecit++){ 73 cout << *vecit << " "; 74 } 75 cout <<endl; 76 sort(vec.begin(),vec.end(),Comp); 77 cout << "降序:"; 78 for(vecit=vec.begin();vecit!=vec.end();vecit++){ 79 cout << *vecit << " "; 80 } 81 cout <<endl; 82 return 0; 83 }
字符串
輸入:
1 #include "stdafx.h" 2 #include <string> 3 #include <iostream> 4 5 using namespace std; 6 int _tmain(int argc, _TCHAR* argv[]) 7 { 8 string s1; 9 s1 = "hello"; 10 string s2; 11 char s[1024]; 12 //scanf輸入速度比cin快的多,但是scanf是c函數,不能輸入字符串 13 scanf("%s",s); 14 s2 = s; 15 cout << s1 << endl; 16 cout << s2 << endl; 17 return 0; 18 }
尾部添加字符字符串直接用+號 例如: s += ‘a‘; s += "abc",或者使用append方法,s.append(“123”)
刪除:
1 // string1.cpp : 定義控制臺應用程序的入口點。 2 // 3 4 #include "stdafx.h" 5 #include <string> 6 #include <iostream> 7 8 using namespace std; 9 int _tmain(int argc, _TCHAR* argv[]) 10 { 11 string s; 12 s = "0123456789"; 13 14 string::iterator it = s.begin(); 15 s.erase(it+3);//刪除s[3] 16 cout << s << endl; 17 18 s = "0123456789"; 19 s.erase(it+1,it+4);//刪除s[1]-s[3] 20 cout << s << endl; 21 22 s.clear(); 23 cout << s << endl; 24 }
查找(find)
用find找到string裏面第一個要找到元素(char或者串),找到返回數組下標,找不到返回end()叠代器
string和vector有很多相同的東西,比如length(),size(),empty(),reverse(),相對也容易,就不一一說了。
數字化處理(string)
經常會遇到這樣一種情況,有一個數字,需要把每一位給提取出來,如果用取余數的方法,花費的時間就會很長,所以可以當成字符串來處理,方便、省時。
例子:求一個整數各位數的和
1 // string1.cpp : 定義控制臺應用程序的入口點。 2 // 3 4 #include "stdafx.h" 5 #include <string> 6 #include <iostream> 7 8 using namespace std; 9 int _tmain(int argc, _TCHAR* argv[]) 10 { 11 string s; 12 s = "0123456789"; 13 int sum = 0; 14 for(int i=0;i<s.size();i++){ 15 switch(s.at(i)){ 16 case ‘1‘:sum+=1;break; 17 case ‘2‘:sum+=2;break; 18 case ‘3‘:sum+=3;break; 19 case ‘4‘:sum+=4;break; 20 case ‘5‘:sum+=5;break; 21 case ‘6‘:sum+=6;break; 22 case ‘7‘:sum+=7;break; 23 case ‘8‘:sum+=8;break; 24 case ‘9‘:sum+=9;break; 25 } 26 } 27 cout << "sum =" << sum << endl; 28 29 }
string與數值相互轉換( sprintf <sstream> )
1 // string1.cpp : 定義控制臺應用程序的入口點。 2 // 3 4 #include "stdafx.h" 5 #include <string> 6 #include <iostream> 7 #include <sstream> 8 using namespace std; 9 10 string converToString(double x){ 11 ostringstream o; 12 if(o << x){ 13 return o.str(); 14 } 15 return "error"; 16 } 17 18 double converFromString(string s){ 19 istringstream i(s); 20 double x; 21 if(i >> x){ 22 return x; 23 } 24 return 0.0; 25 } 26 int _tmain(int argc, _TCHAR* argv[]) 27 { 28 char b[100]; 29 sprintf(b,"%d",1987); 30 string s1 ; 31 s1 = b; 32 cout << s1 << endl; 33 34 string s2 = converToString(1954); 35 cout << s2 << endl; 36 37 string s3 = "202"; 38 int c = converFromString(s3); 39 cout << c << endl; 40 41 string s4 = "casacsa6"; 42 int d = converFromString(s4); 43 cout << d << endl; 44 45 string s5 = "31af12"; 46 int f = converFromString(s5); 47 cout << f << endl; 48 return 0; 49 }
set容器
set是用紅黑樹的平衡二叉索引樹的數據結構來實現的,插入時,它會自動調節二叉樹排列,把元素放到適合的位置,確保每個子樹根節點的鍵值大於左子樹所有的值、小於右子樹所有的值,插入重復數據時會忽略。set叠代器采用中序遍歷,檢索效率高於vector、deque、list,並且會將元素按照升序的序列遍歷。set容器中的數值,一經更改,set會根據新值旋轉二叉樹,以保證平衡,構建set就是為了快速檢索(python中的set一旦建立就是一個常量,不能改的)。
multiset,與set不同之處就是它允許有重復的鍵值。
set的正反遍歷:叠代器iterator,reverse_iterator
1 // string1.cpp : 定義控制臺應用程序的入口點。 2 // 3 4 #include "stdafx.h" 5 #include <set> 6 #include <iostream> 7 8 using namespace std; 9 10 int _tmain(int argc, _TCHAR* argv[]) 11 { 12 set<int> v; 13 v.insert(1); 14 v.insert(5); 15 v.insert(7); 16 v.insert(2); 17 v.insert(4); 18 v.insert(10); 19 20 //中序遍歷=升序遍歷 21 set<int>::iterator it; 22 for(it=v.begin();it!=v.end();it++){ 23 cout << *it << " "; 24 } 25 cout << endl; 26 27 set<int>::reverse_iterator it1; 28 for(it1=v.rbegin();it1!=v.rend();it1++){ 29 cout << *it1 << " " ; 30 } 31 cout << endl; 32 33 return 0; 34 }
自定義比較函數,insert的時候,set會使用默認的比較函數(升序),很多情況下需要自己編寫比較函數。
1、如果元素不是結構體,可以編寫比較函數,下面這個例子是用降序排列的(和上例插入數據相同):
1 // string1.cpp : 定義控制臺應用程序的入口點。 2 // 3 4 #include "stdafx.h" 5 #include <set> 6 #include <iostream> 7 8 using namespace std; 9 10 11 struct Comp 12 { 13 //重載() 14 bool operator()(const int &a, const int &b) 15 { 16 return a > b; 17 } 18 }; 19 int _tmain(int argc, _TCHAR* argv[]) 20 { 21 set<int> v; 22 v.insert(1); 23 v.insert(5); 24 v.insert(7); 25 v.insert(2); 26 v.insert(4); 27 v.insert(10); 28 29 //中序遍歷=升序遍歷 30 set<int>::iterator it; 31 for(it=v.begin();it!=v.end();it++){ 32 cout << *it << " "; 33 } 34 cout << endl; 35 36 set<int,Comp>::reverse_iterator it1; 37 for(it1=v.rbegin();it1!=v.rend();it1++){ 38 cout << *it1 << " " ; 39 } 40 cout << endl; 41 42 return 0; 43 }
map
map也是使用紅黑樹,他是一個鍵值對(key:value映射),便利時依然默認按照key程序的方式遍歷,同set。
multimap
multimap由於允許有重復的元素,所以元素插入、刪除、查找都與map不同。
插入insert(pair<a,b>(value1,value2))
至於刪除和查找,erase(key)會刪除掉所有key的map,查找find(key)返回第一個key的叠代器
deque
deque和vector一樣,采用線性表,與vector唯一不同的是,deque采用的分塊的線性存儲結構,每塊大小一般為512字節,稱為一個deque塊,所有的deque塊使用一個Map塊進行管理,每個map數據項記錄各個deque塊的首地址,這樣以來,deque塊在頭部和尾部都可已插入和刪除元素,而不需要移動其它元素。使用push_back()方法在尾部插入元素,使用push_front()方法在首部插入元素,使用insert()方法在中間插入元素。一般來說,當考慮容器元素的內存分配策略和操作的性能時,deque相對vectore更有優勢。
(下面這個圖,我感覺Map塊就是一個list< map<deque名字,deque地址> >)
插入刪除
遍歷當然可以使用下標遍歷,在這裏使用叠代器。
list
list<int> l
插入:push_back尾部,push_front頭部,insert方法前往叠代器位置處插入元素,鏈表自動擴張,叠代器只能使用++--操作,不能用+n -n,因為元素不是物理相連的。
遍歷:iterator和reverse_iterator正反遍歷
刪除:pop_front刪除鏈表首元素;pop_back()刪除鏈表尾部元素;erase(叠代器)刪除叠代器位置的元素,註意只能使用++--到達想刪除的位置;remove(key) 刪除鏈表中所有key的元素,clear()清空鏈表。
查找:it = find(l.begin(),l.end(),key)
排序:l.sort()
刪除連續重復元素:l.unique() 【2 8 1 1 1 5 1】 --> 【 2 8 1 5 1】
bitset
從來沒用過,上兩幅圖吧就:
stack(後進先出)
這個印象深刻,學數據結構的時候做表達式求值的就是用的棧。
C++中STL介紹