1. 程式人生 > >C++ Primer(第五版) 第九章:順序容器

C++ Primer(第五版) 第九章:順序容器

賦值運算 prime 練習 erase 內部 判斷 相同 編譯 中間

練習9.1:考察使用哪種順序容器

(a)list,當需要在容器中任意位置執行插入/刪除操作時,用list最好

(b)deque,當需要在頭部插入/刪除元素,不需要在容器中間任意位置插入/刪除元素時,用deque最好

(c)vector,當不需要在頭部/任意位置插入/刪除元素的情況下,用vector最好

練習9.2:考察對容器可以保存的元素類型的限制

list<deque<int>>lst1;

list<deque<int> >lst2; //在編譯器較舊版本中的書寫方式

練習9.3:考察叠代器範圍的條件

它們指向同一個容器中的元素(包括尾元素的下一個位置)

end的位置不在begin之前(可以通過反復遞增使得begin到達end)

練習9.4:考察通過叠代器+循環處理一個元素範圍

技術分享圖片
 1 bool find(vector<int>::iterator begin,vector<int>::iterator end,int value)
 2 {
 3     //寫法1: 
 4     while ( begin!=end )
 5     {
 6         if ( *begin==value ) return true;
 7         ++begin;
 8     }
 9     //寫法2:
10
for ( auto iter=begin;iter!=end;++iter ) 11 { 12 if ( *iter==value ) return true; 13 } 14 return false; 15 }
練習9.4

練習9.5:考察同上(當未找到定值時返回尾元素之後的那個位置)

技術分享圖片
 1 vector<int>::iterator find(vector<int>::iterator begin,vector<int>::iterator end,int value)
 2 {
 3     //
寫法1: 4 while ( begin!=end ) 5 { 6 if ( *begin==value ) return begin; 7 ++begin; 8 } 9 //寫法2: 10 for ( auto iter=begin;iter!=end;++iter ) 11 { 12 if ( *iter==value ) return iter; 13 } 14 return end; 15 }
練習9.5

練習9.6:考察叠代器支持的操作

兩個叠代器只能判斷是否相等,不能比較大小

技術分享圖片
1 list<int>lst1;
2 list<int>::iterator iter1=lst1.begin(),lter2=lst1.end();
3 while ( iter1!=lter2 ) /*……*/ 
練習9.6

練習9.7:考察容器類型成員中的類型別名的使用對象

size_type得到容器大小

練習9.8:考察同上

讀取string的list中的元素,使用const_iterator //只讀不寫

寫入list,使用iterator

練習9.9:考察相似叠代器的區別

begin返回容器的iterator類型

cbegin返回容器的const_iterator類型

練習9.10:考察叠代器的類型

在gcc4.8下這段代碼是錯誤的,因為it1和it2的類型是不一樣的

正確的寫法應該是

技術分享圖片
1 auto it1 = v1.begin();
2 auto it2 = v2.begin(), it3 = v1.cbegin(), it4 = v2.cbegin();
View Code

即:只有it1是iterator,剩下對象的類型都是const_iterator

練習9.11:考察vector的創建和初始化

技術分享圖片
1 vector<int>vec1; //不包含值,容器為空
2 vector<int>vec2{1,2,3}; //包含三個值,分別為1,2,3
3 vector<int>vec3={1,2,3}; //包含三個值,分別為1,2,3
4 vector<int>vec4(vec3); //包含三個值,分別為1,2,3
5 vector<int>vec5(vec4.begin(),vec4.end()); //包含三個值,分別為1,2,3
6 vector<int>vec6(3); //包含三個值,均為0 
7 vector<int>vec7(3,1); //包含三個值,均為1 
練習9.11

練習9.12:考察初始化時傳容器和傳兩個叠代器(範圍)的差別

傳容器:要求兩個容器的容器類型和元素類型必須相同,同時得到新容器中元素的範圍和老容器中元素的範圍相同(整個拷貝)

傳兩個叠代器(範圍):不要求容器類型相同,同時只要能將拷貝的元素轉換為要初始化的容器的類型就可以(即新容器和老容器中的元素類型也可以不同),同時可以拷貝容器的子序列(不一定是整個容器)

練習9.13:考察容器的拷貝初始化

技術分享圖片
1 list<int>lst1;
2 vector<double>vec1(lst1.begin(),lst1.end());
3 vector<int>vec2;
4 vector<double>vec3(vec2.begin(),vec2.end());
練習9.13

練習9.14:考察容器的賦值運算(assign)

技術分享圖片
1 list<const char*>lst;
2 vector<string>vec;    
3 vec.assign(lst.cbegin(),lst.cend());
練習9.14

練習9.15:考察相同容器的大小比較

技術分享圖片
1 vector<int>vec1(3,1);
2 vector<int>vec2(3,2);
3 if ( vec1==vec2 ) cout<<"vec1==vec2"<<endl;
4 else cout<<"vec1!=vec2"<<endl;
練習9.15

練習9.16:考察不同容器的大小比較(先統一容器再進行比較)

技術分享圖片
1 vector<int>vec1(3,1);
2 list<int>lst1(3,2);
3 vector<int>vec2(lst1.begin(),lst1.end());
4 if ( vec1==vec2 ) cout<<"vec1==lst1"<<endl;
5 else cout<<"vec1!=lst1"<<endl;
練習9.16

練習9.17:考察容器比較的限制

c1和c2必須是相同類型的容器,且必須保存相同類型的元素

同時且保存的元素必須支持條件運算符的比較

練習9.18:考察deque的push_back操作

技術分享圖片
1 deque<string>container;
2 string word;
3 while ( cin>>word ) container.push_back(word);
4 for ( auto iter=container.cbegin();iter!=container.cend();++iter ) cout<<*iter<<endl;
練習9.18

練習9.19:考察list的push_back操作及其和dequepush_back操作的不同

list、vector和deque的push_back操作類似,只需要改變一下類型名即可

技術分享圖片
1 list<string>container;
2 string word;
3 while ( cin>>word ) container.push_back(word);
4 for ( auto iter=container.cbegin();iter!=container.cend();++iter ) cout<<*iter<<endl;
練習9.19

練習9.20:考察訪問list和向deque中添加元素

技術分享圖片
1 list<int>lst{1,2,3,4};
2 deque<int>odd;
3 deque<int>even;
4 for ( auto i:lst )
5 {
6     if ( i&1 ) odd.push_back(i);
7     else even.push_back(i);
8 }
練習9.20

練習9.21:考察為什麽通過insert的返回值插入這個循環和push_front()等價

技術分享圖片
1 vector<string>vec;
2 auto iter=vec.begin();
3 string word;
4 while ( cin>>word ) iter=vec.insert(iter,word);
練習9.21

剛開始string被添加到begin的位置,因為insert的返回值指向第一個新加入的元素的叠代器(而新加入的元素加在原來容器中begin所在的元素的前一位置,即新加入的元素變成了當前容器的begin),所以insert的返回值是當前容器的begin,每次都將重復這樣的操作,即每次都往容器頭部加入元素。

練習9.22:考察容器添加元素對叠代器的影響

錯誤:(a)循環將一直進行下次,因為iter永遠無法等於mid

(b)添加元素會使mid這個叠代器失效

修改:在添加元素的過程中不斷更新中間叠代器mid、當前叠代器iter和容器大小

技術分享圖片
 1 vector<int>iv{1,1,1,2,3,4};
 2 int cursor=iv.size()/2;
 3 int some_value=1;
 4 auto iter=iv.begin();
 5 auto mid=iv.begin()+cursor;
 6 while ( iter!=mid )
 7 {
 8     if ( *iter==some_value ) 
 9     {
10         iv.insert(iter,2*some_value);
11         ++iter;
12         ++cursor;
13         mid=iv.begin()+cursor;
14     }
15 }
練習9.22

練習9.23:考察當容器中只有一個元素時,訪問首元素和尾元素的結果

val==val2==val3==val4

練習9.24:考察使用不同的方式訪問vector中的第一個元素,以及當容器為空時的訪問結果

技術分享圖片
1 vector<int>vec;
2 vec.at(0);
3 vec[0];
4 vec.front();
5 *(vec.begin());
練習9.24

容器為空時,訪問容器中的元素會產生未定義行為

練習9.25:考察刪除多個元素

當elem1==elem2(==尾後叠代器)時,沒有事情發生

當elem1!=尾後叠代器&&elem2==尾後叠代器時,刪除容器中從elem1所在位置的元素往後所有的元素(包括elem1所在位置的元素)

練習9.26:考察從容器內部刪除元素

技術分享圖片
 1 int ia[]={0,1,1,2,3,5,8,13,21,55,89};
 2 vector<int>vec(ia,end(ia));
 3 list<int>lst(vec.begin(),vec.end());
 4 auto iter1=vec.begin();
 5 while ( iter1!=vec.end() )
 6 {
 7     if ( (*iter1)&1 ) ++iter1;
 8     else iter1=vec.erase(iter1);
 9 }
10 auto iter2=lst.begin();
11 while ( iter2!=lst.end() )
12 {
13     if ( (*iter2)&1 ) iter2=lst.erase(iter2);
14     else ++iter2;
15 }
16 for ( auto i:vec ) cout<<i<<endl;
17 for ( auto i:lst ) cout<<i<<endl;
練習9.26

練習9.27:考察從forward_list中刪除元素

技術分享圖片
 1 forward_list<int>flst={0,1,2,3,4,5,6,7,8,9};
 2 auto prev=flst.before_begin();
 3 auto curr=flst.begin();
 4 while ( curr!=flst.end() )
 5 {
 6     if ( (*curr)&1 ) curr=flst.erase_after(prev);
 7     else
 8     {
 9         prev=curr;
10         ++curr;
11     }
12 }
13 for ( auto i:flst ) cout<<i<<endl;
練習9.27

練習9.28:考察往forward_list中添加元素

技術分享圖片
 1 void find_and_insert(forward_list<string>& list,const string& to_find,const string& to_add)
 2 {
 3     auto prev=list.before_begin();
 4     bool change=false;
 5     for ( auto curr=list.begin();curr!=list.end();prev=curr++ )
 6     {
 7         if ( (*curr)==to_find ) 
 8         {
 9             curr=list.insert_after(curr,to_add);
10             change=true;
11         }
12     }
13     if ( !change ) list.insert_after(prev,to_add);
14 }
練習9.28

練習9.29:考察resize的用法

vec.resize(100)會往rec中再添加75個元素,添加元素的值均為0

vec.resize(10)會使得rec中刪除90個元素只保留下前10個元素

練習9.30:考察resize對容器初始化的影響

當元素的類型是類時,同時類的構造函數沒有提供初始值時不能接受單個參數的resize

練習9.31:考察vector、list和forward_list叠代器操作的不同

list的叠代器沒有+=2這樣的操作,改成advance(iter,2),和+=2等價

技術分享圖片
 1 list<int>vi={0,1,2,3,4,5,6,7,8,9};
 2 auto iter=vi.begin();
 3 while ( iter!=vi.end() )
 4 {
 5     if ( (*iter)&1 )
 6     {
 7         iter=vi.insert(iter,*iter);
 8         advance(iter,2);
 9     }
10     else iter=vi.erase(iter);
11 }
12 for ( auto i:vi ) cout<<i<<endl;
list

forword_list中重點註意兩個叠代器位置的變化

技術分享圖片
 1 forward_list<int>vi={0,1,2,3,4,5,6,7,8,9};
 2 auto prev=vi.before_begin();
 3 auto iter=vi.begin();
 4 while ( iter!=vi.end() )
 5 {
 6     if ( (*iter)&1 )
 7     {
 8         iter=vi.insert_after(prev,*iter);
 9         advance(iter,2);
10         advance(prev,2);
11     }
12     else iter=vi.erase_after(prev);
13 }
14 for ( auto i:vi ) cout<<i<<endl;
forward_list

練習9.32:考察程序規範

不合法,當執行完insert後,iter進行的操作取決於編譯器是不確定的。更詳細的討論請看:https://github.com/Mooophy/Cpp-Primer/issues/125

練習9.33:考察insert的返回值(返回插入的第一個新添加的元素)

insert使得vector的存儲空間被重新分配,導致叠代器失效,會造成程序崩潰

練習9.34:考察insert的返回值

會造成無限循環,當訪問的一個奇數時,會無限訪問該奇數,因為程序遇到奇數會在奇數前添加一個元素,同時叠代器指向該奇數的前一個元素,當叠代器++時又是該奇數

技術分享圖片
 1 #include<iostream>
 2 #include<vector>
 3 using namespace std;
 4 
 5 int main()
 6 {
 7     vector<int>vi={0,1,2,3,4,5,6,7,8,9};
 8     auto iter=vi.begin();
 9     while ( iter!=vi.end() )
10     {
11         if ( (*iter)&1 ) 
12         {
13             iter=vi.insert(iter,*iter);
14             ++iter;
15         }
16         ++iter;    
17     } 
18     for ( auto i:vi ) cout<<i<<endl;
19 }
練習9.34

C++ Primer(第五版) 第九章:順序容器