1. 程式人生 > >C++中STL介紹

C++中STL介紹

沒有 添加數據 pan swap ring 不同之處 結果 allocator 新的

轉載自: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

銷毀所有數據,釋放內存

創建一個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介紹