C++ primer 第9章 顺序容器
文章目錄
- 順序容器類型
- 確定使用哪種順序容器
- 容器庫概覽
- 容器操作
- 迭代器
- 迭代器支持的所有操作
- 迭代器支持的所有運(yùn)算
- 迭代器范圍
- 對(duì)構(gòu)成范圍的迭代器的要求
- 標(biāo)準(zhǔn)庫迭代器范圍左閉右開的三種性質(zhì)
- 容器定義和初始化
- 將一個(gè)新容器創(chuàng)建為另一個(gè)容器的拷貝
- 將array拷貝到vector中的代碼
- 與順序容器大小相關(guān)的構(gòu)造函數(shù)
- 標(biāo)準(zhǔn)庫array具有固定大小
- 賦值和swap
- 測試swap的代碼
- 關(guān)系運(yùn)算符
- 順序容器的特有操作
- 向順序容器添加元素
- 容器元素是拷貝
- 在容器中特定位置添加元素:insert
- emplace操作
- 訪問元素
- 訪問成員函數(shù)返回的是引用
- 刪除元素
- 特殊的forward_list操作
- 改變?nèi)萜鞔笮?/li>
- 容器操作可能使迭代器失效
- vector對(duì)象是如何增長的
- capacity和size
- 額外的string操作
- 構(gòu)造string的其他方法
- substr操作
- 從一個(gè)vector \
順序容器類型
string和vector將元素保存在連續(xù)的內(nèi)存空間中,因此支持快速隨機(jī)訪問,但是在這兩種容器的中間位置添加或刪除元素會(huì)非常耗時(shí),因?yàn)樾枰苿?dòng)插入/ 刪除位置之后的所有元素,來保持連續(xù)存儲(chǔ)。而且添加一個(gè)元素有時(shí)可能還需要分配額外的存儲(chǔ)空間,這種情況下,每個(gè)元素都必須移動(dòng)到新的存儲(chǔ)空間中。
list和forward_list兩個(gè)容器添加和刪除操作快速,但不支持元素的隨機(jī)訪問,為了訪問一個(gè)元素只能遍歷整個(gè)容器,其存儲(chǔ)的內(nèi)存空間不連續(xù)。
deque支持快速隨機(jī)訪問,中間位置添加或刪除元素非常耗時(shí),但是在deque的兩端添加或刪除元素很快。
確定使用哪種順序容器
通常,使用vector是最好的選擇,除非你有很好的理由選擇其他容器。
容器庫概覽
一般來說,每個(gè)容器都定義在一個(gè)頭文件中,文件名與類型名相同,即deque定義在頭文件deque中,list定義在頭文件list中,以此類推。容器均定義為模板類,例如對(duì)vector,我們必須提供額外信息來生成特定的容器類型。對(duì)大多數(shù),但不是所有容器,我們還需要額外提供元素類型信息:
list<Sales_data> 保存Sales_data對(duì)象的list
deque<double> 保存double的deque
順序容器幾乎可以保存任意類型的元素。特別是,我們可以定義一個(gè)容器,其元素的類型是另一個(gè)容器。這種容器的定義與任何其他容器類型完全一樣:
vector<vector<string>> lines; 此處lines是一個(gè)vector,其元素類型是string的vector
容器操作
迭代器
迭代器支持的所有操作
forward_list迭代器不支持遞減運(yùn)算符
迭代器支持的所有運(yùn)算
這些運(yùn)算只適用于string、vector、deque和array的迭代器,我們不能將它們用于其他任何容器類型的迭代器。
例如,list的迭代器不支持<運(yùn)算,只支持遞增、遞減、==以及!=運(yùn)算,原因在于list是將元素以鏈表方式存儲(chǔ),在內(nèi)存中不連續(xù),兩個(gè)指針的大小關(guān)系與它們指向的元素的前后關(guān)系并不一定是吻合的,實(shí)現(xiàn)<運(yùn)算將會(huì)非常困難和低效。
迭代器范圍
一個(gè)迭代器范圍由一對(duì)迭代器表示,兩個(gè)迭代器分別指向同一個(gè)容器中的元素或者是尾元素之后的位置。這兩個(gè)迭代器通常被稱為begin和end
對(duì)構(gòu)成范圍的迭代器的要求
如果滿足如下條件,兩個(gè)迭代器begin和end構(gòu)成一個(gè)迭代器范圍:
它們指向同一個(gè)容器中的元素,或者是容器最后一個(gè)元素之后的位置,且我們可以通過反復(fù)遞增begin來到達(dá)end。換句話說,end不在begin之前。
標(biāo)準(zhǔn)庫迭代器范圍左閉右開的三種性質(zhì)
假定begin和end構(gòu)成一個(gè)合法的迭代器范圍,則
- 如果begin與end相等,則范圍為空
- 如果begin與end不相等,則范圍至少包含一個(gè)元素,且begin指向該范圍中的第一個(gè)元素
- 我們可以對(duì)begin遞增若干次,使得begin==end
容器定義和初始化
每個(gè)容器類型都定義了一個(gè)默認(rèn)構(gòu)造函數(shù)。除array之外,其他容器的默認(rèn)構(gòu)造函數(shù)都會(huì)創(chuàng)建一個(gè)指定類型的空容器,且都可以接受指定容器大小和元素初始值的參數(shù)。
將一個(gè)新容器創(chuàng)建為另一個(gè)容器的拷貝
為了創(chuàng)建一個(gè)容器為另一個(gè)容器的拷貝,兩個(gè)容器的類型及其元素類型必須匹配。不過,當(dāng)傳遞迭代器參數(shù)來拷貝一個(gè)范圍時(shí),就不要求容器類型是相同的了,而且新容器和原容器中的元素類型也可以不同,只要能將要拷貝的元素轉(zhuǎn)換為要初始化的容器的元素類型即可。
將array拷貝到vector中的代碼
int ia[] = { 0,1,1,2,3,5,8,13,21,55,89 }; vector<int>vect; vect.assign(ia,ia+11);與順序容器大小相關(guān)的構(gòu)造函數(shù)
如果元素類型是內(nèi)置類型或者是具有默認(rèn)構(gòu)造函數(shù)的類類型,可以只為構(gòu)造函數(shù)提供一個(gè)容器大小參數(shù)。如果元素類型沒有默認(rèn)構(gòu)造函數(shù),除了大小參數(shù)外,還必須指定一個(gè)顯式的元素初始值。
只有順序容器的構(gòu)造函數(shù)才接受大小參數(shù),關(guān)聯(lián)容器并不支持。
標(biāo)準(zhǔn)庫array具有固定大小
與內(nèi)置數(shù)組一樣,標(biāo)準(zhǔn)庫array的大小也是類型的一部分。當(dāng)定義一個(gè)array時(shí),除了指定元素類型,還要指定容器大小,例如array<int,42>
與其他容器不同,一個(gè)默認(rèn)構(gòu)造的array是非空的:它包含了與其大小一樣多的元素。這些元素都被默認(rèn)初始化。如果我們對(duì)array進(jìn)行列表初始化,則初始值的數(shù)目必須等于或小于array的大小。如果初始值數(shù)目小于array的大小,則它們被用來初始化array中靠前的元素,所有剩余元素都會(huì)進(jìn)行值初始化。在這兩種情況下,如果元素類型是一個(gè)類類型,那么該類必須有一個(gè)默認(rèn)構(gòu)造函數(shù),以使值初始化能夠進(jìn)行。
我們不能對(duì)內(nèi)置數(shù)組類型進(jìn)行拷貝或?qū)ο筚x值操作,但array并無此限制:
賦值和swap
與內(nèi)置數(shù)組不同,標(biāo)準(zhǔn)庫array類型允許賦值,賦值后左右兩邊的運(yùn)算對(duì)象需具有相同的類型:
array<int, 10>a1 = {0,1,2,3,4,5,6,7,8,9}; array<int, 10>a2 = { 0 }; a1 = a2;//此時(shí)a1中的元素為10個(gè)0
除array外,swap不對(duì)任何元素進(jìn)行拷貝、刪除或插入操作,因此可以保證在常數(shù)時(shí)間內(nèi)完成。
除string外, 指向容器的迭代器、引用和指針在swap操作之后都不會(huì)失效,它們?nèi)灾赶騭wap操作之前所指向的那些元素,但是在swap之后,這些元素已經(jīng)屬于不同的容器了,詳情可見對(duì)vector進(jìn)行測試的代碼。與其他容器不同,swap兩個(gè)array會(huì)真正交換它們的元素,因此,對(duì)于array,在swap操作之后,指針、引用和迭代器所綁定的元素保持不變,但元素值已經(jīng)與另一個(gè)array中對(duì)應(yīng)元素的值進(jìn)行了交換,詳情可見對(duì)array進(jìn)行測試的代碼。
測試swap的代碼
對(duì)array進(jìn)行測試的代碼
int main() {array<int, 10>a1 = {1};array<int, 10>a2 = {0};auto beg1 = a1.begin();auto beg2 = a2.begin();cout << "swap之前:" << endl;cout << "a1元素:";for (auto a : a1) {cout << a << " ";}cout << endl;cout << "a2元素:";for (auto a : a2) {cout << a << " ";}cout << endl;cout << "a1.begin:"<< *beg1 << endl;cout << "a2.begin:" << *beg2 << endl;swap(a1,a2);cout << "swap之后:"<<endl;cout << "a1元素:";for (auto a : a1) {cout << a << " ";}cout << endl;cout << "a2元素:";for (auto a:a2) {cout << a << " ";}cout << endl;cout << "a1.begin:" << *beg1 << endl;cout << "a2.begin:" << *beg2 << endl;system("pause");return 0; }測試結(jié)果:
swap之前: a1元素:1 0 0 0 0 0 0 0 0 0 a2元素:0 0 0 0 0 0 0 0 0 0 a1.begin:1 a2.begin:0 swap之后: a1元素:0 0 0 0 0 0 0 0 0 0 a2元素:1 0 0 0 0 0 0 0 0 0 a1.begin:0 a2.begin:1對(duì)vector進(jìn)行測試的代碼:
int main() {vector<int>a1 = {1};vector<int>a2 = {0};auto beg1 = a1.begin();auto beg2 = a2.begin();cout << "swap之前:" << endl;cout << "a1元素:";for (auto a : a1) {cout << a << " ";}cout << endl;cout << "a2元素:";for (auto a : a2) {cout << a << " ";}cout << endl;cout << "a1.begin:"<< *beg1 << endl;cout << "a2.begin:" << *beg2 << endl;swap(a1,a2);cout << "swap之后:"<<endl;cout << "a1元素:";for (auto a : a1) {cout << a << " ";}cout << endl;cout << "a2元素:";for (auto a:a2) {cout << a << " ";}cout << endl;cout << "a1.begin:" << *beg1 << endl;cout << "a2.begin:" << *beg2 << endl;system("pause");return 0; }測試結(jié)果:
swap之前: a1元素:1 a2元素:0 a1.begin:1 a2.begin:0 swap之后: a1元素:0 a2元素:1 a1.begin:1 a2.begin:0關(guān)系運(yùn)算符
每個(gè)容器都支持相等運(yùn)算符(==和 !=),除了無序關(guān)聯(lián)容器外的所有容器都支持關(guān)系運(yùn)算符(>,>=,<,<=),關(guān)系運(yùn)算符左右兩邊的運(yùn)算對(duì)象必須是相同類型的容器,且必須保存相同類型的元素。
比較兩個(gè)容器實(shí)際上是進(jìn)行元素的逐對(duì)比較:
- 如果兩個(gè)容器具有相同大小且所有元素都兩兩對(duì)應(yīng)相等,則這兩個(gè)容器相等;否則兩個(gè)容器不等
- 如果兩個(gè)容器大小不同,但較小容器中每個(gè)元素都等于較大容器中的對(duì)應(yīng)元素,則較小容器小于較大容器
- 如果兩個(gè)容器都不是另一個(gè)容器的前綴子序列,則它們的比較結(jié)果取決于第一個(gè)不相等的元素的比較結(jié)果
順序容器的特有操作
向順序容器添加元素
除array外,所有標(biāo)準(zhǔn)庫容器都提供靈活的內(nèi)存管理,在運(yùn)行時(shí)可以動(dòng)態(tài)添加或刪除元素來改變?nèi)萜鞔笮 ?br />
容器元素是拷貝
當(dāng)我們用一個(gè)對(duì)象來初始化容器時(shí),或?qū)⒁粋€(gè)對(duì)象插入到容器中時(shí),實(shí)際上放入到容器中的是對(duì)象值的一個(gè)拷貝,而不是對(duì)象本身。容器中的元素與提供值的對(duì)象之間沒有任何關(guān)聯(lián),隨后對(duì)容器中元素的任何改變都不會(huì)影響到原始對(duì)象,反之亦然。
在容器中特定位置添加元素:insert
在新標(biāo)準(zhǔn)下,接受元素個(gè)數(shù)或范圍的insert版本返回指向第一個(gè)新加入元素的迭代器,如果范圍為空,不插入任何元素,insert操作會(huì)將第一個(gè)參數(shù)返回。
通過使用insert的返回值,可以在容器中一個(gè)特定位置反復(fù)插入元素。
emplace操作
訪問元素
訪問成員函數(shù)返回的是引用
在容器中訪問元素的成員函數(shù)(即,front、back、下標(biāo)和at)返回的都是引用。如果容器是一個(gè)const對(duì)象,則返回值是const的引用。如果容器不是const的,則返回值是普通引用,我們可以用來改變?cè)氐闹?#xff1a;
刪除元素
特殊的forward_list操作
改變?nèi)萜鞔笮?/h2>
示例代碼如下:
容器操作可能使迭代器失效
向容器中添加元素和從容器中刪除元素的操作可能會(huì)使指向容器元素的指針、引用或迭代器失效。一個(gè)失效的指針、引用或迭代器將不再表示任何元素。使用失效的指針、引用或迭代器是一種嚴(yán)重的程序設(shè)計(jì)錯(cuò)誤,很可能會(huì)引起與使用未初始化指針一樣的問題。
如果在一個(gè)循環(huán)中插入/刪除deque、string或vector中的元素,不要緩存end返回的迭代器,必須在每次操作后重新調(diào)用end(),而不能在循環(huán)開始前保存它返回的迭代器。
vector對(duì)象是如何增長的
reserve并不改變?nèi)萜髦性氐臄?shù)量,它僅影響vector預(yù)先分配多大的內(nèi)存空間。
capacity和size
容器的size是指它已經(jīng)保存的元素的數(shù)目;而capacity則是在不分配新的內(nèi)存空間的前提下它最多可以保存多少元素。
額外的string操作
除了順序容器共同的操作之外,string類型還提供了一些額外的操作,這些操作中的大部分要么是提供string類和c風(fēng)格字符數(shù)組之間的相互轉(zhuǎn)換,要么是增加了運(yùn)行我們用下標(biāo)代替迭代器的版本。
構(gòu)造string的其他方法
代碼示例:
substr操作
從一個(gè)vector <char> 初始化一個(gè)string
vector提供了一個(gè)data成員函數(shù),返回其內(nèi)存空間的首地址
vector<char>vi{ 'a','b' ,'c','d'}; string s(vi.begin(),vi.end()); string s2(vi.data(),vi.size());改變string的其他方法
string的下標(biāo)版本的insert和erase版本
string類型支持順序容器的賦值運(yùn)算符以及assign、insert和erase操作,除此之外,它還定義了下標(biāo)版本的insert和erase版本:
string s="0123456789";s.insert(5, 3, 'A'); //此時(shí)s是01234AAA56789,即在s[5]之前插入3個(gè)‘A’string s1 = "0123456789";s1.erase(5, 3);//此時(shí)s1是0123489,即刪除s[5]之后的3個(gè)元素,包括s[5]string的c風(fēng)格字符數(shù)組版本的insert和erase版本
const char *cp = "0123456789";string s;s.assign(cp,5); //s=="01234"s.insert(s.size(),cp+7);s=="01234789" string s = "some string";string s2 = "some other string ";s.insert(0,s2);//s=="some other string some string"// 在s[0]之前插入s2中s2[0]開始的s2.size()個(gè)字符s.insert(0,s2,0,s2.size());//s=="some other string some other string some string"append和replace函數(shù)
string s="some "; s.append("things"); //s=="some things" s.replace(5,3,"hhhh");//s=="some hhhhngs"即從s[5]開始將3個(gè)元素,替換為“hhhh”string搜索操作
string搜索操作,每個(gè)操作都返回一個(gè)string::size_type值,表示匹配位置的下標(biāo)。如果搜索失敗,則返回一個(gè)名為string::npos的static成員。
string find
find查找參數(shù)指定的字符串,若找到,則返回第一個(gè)匹配位置的下標(biāo),否則返回npos
string name("AnnaBelleAnnaBelle"); auto pos1=name.find("Anna");//pos1==0 auto pos2 = name.find("Anna",1);//pos2==9 此處是從name[1]處開始查找"Anna",返回第一個(gè)匹配位置的下標(biāo)string find_first_of
string numbers("0123456789");string name("n1inin4n");auto pos1 = name.find_first_of(numbers,2);//pos1==6auto pos1 = name.find_first_of(numbers);//pos1==1string compare函數(shù)
根據(jù)s是等于、大于還是小于參數(shù)指定的字符串,s.compare返回0、正數(shù)或負(fù)數(shù)。
數(shù)值轉(zhuǎn)換
int i = 42;string s = to_string(i);//將整數(shù)i轉(zhuǎn)換為字符“42”double num = stod(s);// 將字符“42”轉(zhuǎn)換為浮點(diǎn)數(shù)42 s = "00110011";int num = stoi(s,0,2);// 將s轉(zhuǎn)換為二進(jìn)制,num==51容器適配器
除了順序容器外,標(biāo)準(zhǔn)庫還定義了三個(gè)順序容器適配器:stack、queue和priority_queue。
默認(rèn)情況下,stack和queue是基于deque實(shí)現(xiàn)的,priority_queue是在vector之上實(shí)現(xiàn)的。
適配器是標(biāo)準(zhǔn)庫中的一個(gè)通用概念,容器、迭代器和函數(shù)都有適配器。本質(zhì)上,一個(gè)適配器是一種機(jī)制,能使某種事物的行為看起來像另外一種事物一樣。一個(gè)容器適配器接受一種已有的容器類型,使其行為看起來像一種不同的類型。
所有容器適配器都支持的操作和類型
棧適配器
stack定義在stack頭文件中,先進(jìn)后出
隊(duì)列適配器
queue和priority_queue定義在queue頭文件中。
queue,先進(jìn)先出
priority_queue,允許我們?yōu)殛?duì)列中的元素建立優(yōu)先級(jí),新加入的元素會(huì)排在所有優(yōu)先級(jí)比它低的已有元素之前。默認(rèn)情況下,標(biāo)準(zhǔn)庫在元素類型上使用<運(yùn)算符來確定相對(duì)優(yōu)先級(jí)。
總結(jié)
以上是生活随笔為你收集整理的C++ primer 第9章 顺序容器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 学生办储蓄卡还是借记卡 适合储蓄卡
- 下一篇: c++面向对象高级编程 学习三 堆、栈