STL中的順序容器有:
vector //支持快速隨機訪問
list //支持快速插入刪除
deque //雙端隊列
順序容器適配器:
stack //LIFO
queue //FIFO
priority_queue //有優先級管理的隊列
我不是很明白adapter適配器的意思。這個詞太布宜諾斯艾利斯了。以前接觸到的有電源適配器,網絡適配器,等等,在我的腦子裏直接會被替換成另一個詞彙,比如變壓器,網卡之類的。估摸着大概就是特殊應用的工具這種意思。
言歸正傳, 重複造輪子一直以來都是我樂此不疲的事情,所以此番是第一次學習STL。
順序容器,按字面意思就是把一堆東西(按模板的定義)按順序存放起來。
作爲一個初學者,使用最多的就是數組。但是數組需要在初始化的時候就設置好大小。如果是動態會增加刪除的需求的話,就不好使用數組了。這個時候可以使用vector來替代。
一, vector
使用vector之前,需要引入它的頭文件
#include <vector>
然後是
using std::vector;
當然,不限麻煩可以不寫這句,只要可以在每次聲明vector的時候都std::vector<XXX> vec;
#include <iostream> using namespace std; #include <vector> using std::vector; struct Student { int nCode; string strName; }; int main() { Student sts[] = {{4, "ddd"},{2, "bbb"},{6, "fff"},{3, "ccc"},{1, "aaa"},{5, "eee"}}; vector<Student> vec(sts, sts+sizeof(sts)/sizeof(Student)); vector<Student>::iterator iter = vec.begin(); for(;iter!=vec.end();iter++){ cout<<iter->nCode<<", "<<iter->strName<<endl; } system("pause"); return 0; }
vector有好幾個構造函數,其中一個可以很方便地將數組的副本轉化成vector,也可以從數組中選擇一個區間將副本轉換成vector。
然後使用iterator(迭代器)或者是下標可以快速地對容器中的元素進行訪問。
對,只是訪問。和數組一樣,下標只是用於訪問,增刪什麼的使用下標或是迭代器都是很大的錯誤哦。
使用迭代器的時候,如果要訪問第一個元素,可以使用vector的成員函數begin(),它將返回第一個元素的迭代器。然後使用*符號就能訪問第一個元素。簡直就像是一個指針嘛。使用成員函數end()將會返回最後一個元素的末尾,這個末尾只是表示迭代器到達容器尾端,並不是最後一個元素,可以用來判斷容器中的元素遍歷完畢。
對迭代器使用算術操作符可以調整迭代器的定位。比如要快速定位到中間的元素,可以這樣:
iter = vec.begin()+vec.size()/2;
對vector元素使用push_back可以在末尾插入元素,其他諸如此類的方法,等需要用的時候自行選擇即可。
需要注意的是,每次對vector的元素進行增刪之後可能引起迭代器定位的變動,再次使用迭代器之前需要重新定位。
二, list
同vector一樣,使用前需要引入頭文件
#include <list>
using std::list;
list的操作方式和vector大同小異。比較大的區別就是對數據的快速增刪上面,顧名思義。
還有好用的就是,list多了一個sort成員函數,對元素進行排序。
上面的代碼示例中在容器裏亂序添加了幾個學生,下面,替換成list容器,並對他們進行排序。
#include <iostream> using namespace std; #include <list> using std::list; struct Student { int nCode; string strName; }; bool Compfn(Student x,Student y) { if(x.nCode>=y.nCode) return true; else return false; } int main() { Student sts[] = {{4, "ddd"},{2, "bbb"},{6, "fff"},{3, "ccc"},{1, "aaa"},{5, "eee"}}; list<Student> lst(sts, sts+sizeof(sts)/sizeof(Student)); //list<Student>::iterator iter = lst.begin();//在這裏的迭代器在排序之後定位會被更改 lst.sort(Compfn); list<Student>::iterator iter = lst.begin(); for(;iter!=lst.end();iter++){ cout<<iter->nCode<<", "<<iter->strName<<endl; } system("pause"); return 0; }
sort函數是按照升序排序的, 如果已經有<符號類型的話,可以直接使用lst.sort()進行排序,沒有的話需要實現一下。
sort函數可以接受一個參數,像學生等自定義的類型無法判斷按什麼規則來排序,可以按照姓名的拼寫,可以按照學號,可以升序,可以降序等等。。。這個時候定義一個函數來決定這個規則,這個函數指針就是sort的參數。
比如上例的Compfn函數
該函數接受兩個參數, 前者x和後者y, 如果返回爲true則不會變換兩者的位置表示順序正確,反之返回false則表示順序不對,需要變換位置。此處可以更改邏輯,自定義排序規則,比如>=的時候返回true就是降序平排列了。
另外, 似乎list沒有提供下標訪問。要問爲什麼的話,此時的我只想使用STL,並不想看源碼,顧名思義鏈表的話隨機訪問的確不是很方便。
同理得其他
另外的順序容器也都差不多。最後談一下我學到的幾個小教訓。
在使用stack的時候想要遍歷stack,於是訪問一次top,彈出一個元素,同時還對計數器i進行++操作。結果查看的元素只有一半。。。這個計數器i++的操作真是多餘,是不動大腦的機械式勞動的產物。
還有一個bitset的東西很有意思, 因爲沒有系統學習泛型, 所以是第一次看到將泛型當做類似函數參數一樣使用的。
原來泛型的還可以寫成bitset<32>這樣。。。爲什麼不乾脆設計成普通類的構造函數呢?這很奇怪。還有這樣的泛型也自己模仿着寫了一下,發現使用template<int n>這樣是沒問題,但如果是自定義類型的話(比如template<Student stu>)就不行。
template<Student stu> class Person { int m_numb; public: Person() { m_numb = stu->nCode; cout<<m_numb; } }; int main() { Student stu = {10}; Person<stu> p;//error return 0; }
要說爲什麼的話:
可以去學習下C++primer的16.2.1節。
修改成如下就沒問題了。
template<Student* stu> class Person { int m_numb; public: Person() { m_numb = stu->nCode; cout<<m_numb<<endl; } }; Student stu = {10}; int main() { stu.nCode = 100; Person<&stu> p; system("pause"); return 0; }